Macros kinda terrify me because they mean I can't
glance at a random 5-line fragment and know exactly
what's happening
Higher-level languages like Java running on top of advanced VMs like the Hotspot JVM -- are high-level enough such that you NEVER know what's happening.Even dropping to JVM bytecode doesn't help you much, as that doesn't show you how data moves in and out of the registers, how decisions on compiling hotspots / inlining functions are made, or how allocated objects move through GC-generations. You also lose control a lot. When you drop down to assembly, tail-calls are trivial for instance, not to mention dealing with cache pressure becomes a lot simpler.
What high-level languages give you (versus assembly) is a way to express intent, rather than details that you don't care about in general.
Which is no different from having macros in your language, or other high-level abstractions, in which you invent your own language for expressing intent, rather than deal with useless details that you don't care about 99% of the time.
Foo foo = foos.next()
But that line isn't really clear. It's saying stuff like - give me a Foo right now. But what happens when there are no more Foos to be given? Does it return a Null? Does it trigger an exception? That line of code also doesn't express intent. So you've got a Foo. Now what? And have I mentioned that objects in "foos" may not be Foo instances?Basically that whole line of code is totally worthless boilerplate.