One thing I'll miss about this was the way this arcane writing increasingly made sense over time as you became a better programmer.
I learned Java only after Python and I remember being not quite familiar with types so even the 'void' and 'String[]' where a bit mysterious. After learning basic types that part made sense. Then you start learning about Classes and objects, and you understood that main is a static method of this class Main that must be required somehow. As you dive deeper you start learning when this class is called. In a weird way what started as complete, unknowable boiler plate slowly evolved into something sensible as you began to understand the language better. I have no doubt that seasoned Java devs see a lot more in that invocation that I do.
Good riddance indeed. The last 30 years of software teaching basically trained developers to produce complexity for its own sake, while calling it engineering. I'm not sufficiently full of myself to link to my own writing (yet) but I'm full of myself enough to self-paraphrase:
1. Programmer A creates a class because they need to do create an entry point, a callback, an interface... basically anything since everything requires a class. Result: we have an class.
2. Programmer B sees a class and carelessly adds instance variables, turning the whole thing mutable. Result: we have an imperative ball of mud.
3. Another programmer adds implementation inheritance for code reuse (because instance variables made factoring out common code into a function impossible without refactoring to turn instance variables from step 2 into arguments). Result: we have an imperative ball of mud and a nightmare of arbitrary dynamic dispatch.
At some point reference cycles arise and grandchild objects hold references to their grandparents in order to produce... some flat dictionary later sent over the wire.
4. As more work is done over that bit of code, the situation only worsens. Refactoring is costly and tedious, so it doesn’t happen. Misery continues until code is removed, typically because it tends to accumulate inefficiencies around itself, forcing a rewrite.
All of these things you've described are actions taken by someone that has not had a deep think about their code and organization, architecture and patterns. It reeks of inexperience, and/or pressure to get shit done without needing any time/deep thinking on how best to do it.
While the teaching is partially to blame, i say it is more that most people are sloppy and undisciplined thinkers. When they dont have any incentive to produce disciplined code, they wont.
Except a static class method is just a hand-wavy way of denying reality: that a program has a single entry point that has nothing to do with classes
As much as C++ has a lot of problems, them and other languages (python/ruby/etc) never denied that the procedural world existed, while Java wants to blindfold you and push you through a corridor until you get out of it and into the "perfect (not) OOP world"
But in Java, a program doesn't have a single static entry point. You can have as many different classes as you want, each with its own static void main, and choose which to use as the entry point when you start up your JVM.
> python/ruby/etc never denied that the procedural world existed
Interestingly, those two languages use very different mechanisms for top-level functions - although I’m not sure if there’s a significant difference in practice.
Python has true standalone functions, whereas in Ruby’s they’re really methods of a `main` object.
I remember our "intro to programming course at university" started with this program and the tutor said "I'm mostly gonna try and explain all the code I show you, as I show it, but this is the exception, you just have to accept this nonsense bit for now".
And yeah, it was pretty cool looking back on it later and realising I knew what it all meant!
That's just Stockholm Syndrome. Think about it: what more crazy garbage could you add that is useless for the beginner but "will make sense later"?
The answer is: infinitely much. You could have to write each character in strings as separate characters for example. It would be absolute utter madness but it "would make sense later".
Yup... if you're familiar with the old class-based approach, the new style raises more questions than it answers. Did they really turn "everything-is-an-object"-Java into a procedural language? What do you do if you do need command line arguments? Etc.
(I somehow mostly avoided learning Java, and my last passing contact with it was 20+ years ago, so these are honest questions).
In general, Java made OO design trivial to implement, but due to a clear specification standard requirement a lot more planning/time is needed.
Thus, many sub-optimal project coordination approaches ended up fighting the design patterns. Java is a good student language, but there are so many better options now... with fewer footguns, and without Oracles farts. =3
Java is essentially open source enough that you can ignore oracle. All the development of java happen in openjdk, of which oracle is a contributor (along with red hat and many others). Oracle’s java is just an openjdk distribution, with some additional proprietary bits.
Pulling the oracle card when java is mentioned is a useless stunt.
Yep, same for me, it's been a long time since I did java and while the publicstaticvoidmain mantra felt familiar I was a bit confused by the Scanner thing and couldn't say why.
My experience may be different from the rest of people at HN. But I am used to work in large projects that last decades. How main is written is totally irrelevant for my day to day work or my career.
Or if you're an Android developer then main doesn't exist at all and never has
But yeah I don't understand why the author is so excited about this. How "ugly" a trivial "Hello, World!" does not really matter much and isn't a good indication of anything about the language's ability to handle more than "Hello, World!"
I think it does say something about what a language thinks is important.
Java (historically) is famous for going a bit too hard core into overly abstract & verboseobject oriented design patterns - create a factory to get another factory to get a different factory and whatnot. The hello world is where java shows that style of code is what historical java felt was the ideal.
>How "ugly" a trivial "Hello, World!" does not really matter much and isn't a good indication of anything about the language's ability to handle more than "Hello, World!"
Sure, but for beginner programmers who don't have the discipline down yet, it's unnecessarily hard. I bought a Java programming book as a kid and got stuck because of a typo that produced an error message I couldn't understand. This was the time before StackOverflow and Reddit. In retrospect, this delayed my programming journey by at least a year.
Longer Hello Worlds make frustration and getting stuck like this more likely.
I learnt C at Uni (after having taught myself BASIC, Z80 machine code (not assembly), and x86 assembly), when we were taught C it was explained to us what all that sort of thing meant. But having said that most of the class failed to understand.
Now we can type out the same semantics, and remain clueless about what it means, but with a new obfuscated syntax which stops us from asking about the semantics.
Probably not in the least for developers, but it does affect beginners a lot.
I used to teach Java, and the total sum of magic incantations you needed before starting to actually "Hello, world" were an instant turn off for many students.
Java sometimes has a culture of "the proper way to do this is to take all these little pieces and put them together in a specific way" which probably makes sense for an object oriented purist but is a bit of a drag day to day.
The scanner class is one such thing, but you also have things like wrapping a Reader in a BufferedReader to add buffering, and building a Pattern object to build a Matcher object which has a few different methods you can call to actually do anything useful
It is very OO, but it's also a bit annoying, and more modern java libs tend to give you a more comfortable API. Sadly modern java also tends to come with springboot and people who can't do anything, unless they use springboot
> Sadly modern java also tends to come with springboot and people who can't do anything, unless they use springboot
Spring Boot (and Spring generally) is more of a “Java culture” issue — someone on this thread used the term “framework fetishism” and it’s spot on.
Thankfully there are teams, typically in mobile or polyglot microservices environments, who are moving away from this. But yeah — still far too common.
It's a shame that they only introduced this as a feature only for single-file programs.
It would make Java a much better language in my eyes if it allowed package-level methods, or even multiple records/classes per file. But apparently "impact on how Java code is written in general" is not wanted.
I'm someone who wrote a lot of 'raw' Java and C++ without frameworks, which meant there was a lot less boilerplate and indirection etc. And for me, the big usability boon came about 10 years ago, around the same time for both Java and C++, when they started supporting lambda functions. I started passing code into algorithms and, although it sounds quite complicated, it simplified my programs substantially. Before that syntactic sugar gave the affordance I hadn't been organising my programs that way.
For the longest time Java had been very keen to be explicit, to the point of needing a log of cruft where it just felt like the compiler was checking you spelt every letter of the incantation correctly because both the compiler and you knew unambiguously what simple thing you were trying to achieve. But this tone changed in Java around the time the new entrants like Kotlin started taking over as what you develop in in a way that scala and clojure never managed. And suddenly all the get-out-of-your-way of lambdas saving you typing out classes and anonymous classes arrived and Java became a much nicer experience imo.
Now if we could just wean some more of the ecosystem away from the framework fetishism... there's no reason java programs have to be so huge, unwieldy and slow to start.
No reason apart from the framework epidemic that's supposed to make things easier for developers, but in the end hamstrings them and weighs everything down.
(to the downvoters - I am a java developer and this is very real to me at the moment!)
In fact, it was welcomed in open arms even being originally interpreted, because writing portable C or C++ code in 1996 was still a mess, even across UNIX flavours.
On my university no one got Sun's marketing money in 1998, yet the distributed systems, compilers design, and graphics programming, all adopted Java as the language for new teaching materials, as it sorted out several problem with the assignments.
Only bad experience I remember from Java was the int/Integer primitive/object issue and boxing.
Also of course the misuse and overuse of classes when designing systems, but the complexity at design time is also the counterpart of the ease of use of well designed APIs. I remember using an IDE to write some code and it skipped the whole documentation and run/compile error phase, I could just cast mismatching types as needed.
Probably C# and Swift/Objective C would only be above Java as top languages, being actually funded by paying customers instead of used by companies that depend on volunteer developed open source software and end-users that equate paying for software with evilness.
It's not a terrible language, but it's not too good either.
For a very long time it completely stagnated and all the features we see added now should've been there 10 years ago, at least, and a bunch of other improvements on top. All because "our goal isn't to adopt the strategy of less successful products, but to forge our own" which now turned into busily adopting all the features that the "less successful products" have had for years: https://news.ycombinator.com/item?id=28985688
I really hope you mean this as it has (amazing backwards compatibility) and (package management), because describing the package management in Java as anything more than "I guess it's better than C" feels overly generous
As a java guy and think python is weird, I don't think this sucks.
But, I also agree that can serve as terrible intro to programming if you start programming right away without understanding the basics of abstractions. But, often when we have tools either designed for a purpose in mind or a dominant paridigm or reaction to existing set of tooling, this can result in understandable yet extreme abstractions.
Java is designed with OOP in mind and it kind of makes sense to have the user to think in terms of lego blocks of interfaces. Every method or class needs to have clear understanding of its users.
public - software handle is for all users
protected - software handle for current and extending classes
default - software is exposed to current package
private - software is restricted to be used in current class alone and nowhere else
So, the beginning of java programming starts with interface exposed to the user or other programmers. Is it weird and extreme. Yes. At least, it is consistent.
This heavyweight syntax has nothing do with OOP. No common definition of OOP (before Java came into existence) said that functions cannot exist outside of a class.
Java choose to adopt object oriented purity in a very weird way. Sun has initially rejected the idea of standalone functions (static import was introduced in Java 5, closures in Java 8 and compact source files/instance main is only coming now). Even with static imports and compact source files, there is still a class holding these methods behind the scenes (for practical and compatibility reasons, more than to achieve any ideological or theoretical goals at this point). That seems like Sun was trying to keep the purity of "everything is an object", but at the same time they've introduced primitive values, which are clearly not objects. This was a pretty bad decision in my opinion. There is no practical benefit from requiring every function to be defined inside a class[1]. On the other hand, being able to have methods defined on an integer is a pretty nice feature.
If we look at a Smalltalk, which is generally considered be an example of a "pure" OOP language, we see a striking difference. This is Hello World in Smalltalk:
'Hello, world!' printNl
Smalltalk allows you to freely define functions outside of classes and even write code that is not part of a class (directly in the REPL).
I am not sure of historical significance of what OOP is, but even Alan Kay seem to agree that modern definition of OOP is not what he intended[1]. But, for better or worse we are stuck with the principles.
We even have design patterns like Command, to workaround first class functions in "pure" OOPy way.
And for enterprise software development, I like it that way. It can make up a definition it wants and stick to it. I think it is better for a language's ecosystem and culture to have one dominant paradigm than becoming kitchen sink of programming languages.
I agree, this is the whole point of "Hello world", which is to show the boilerplate required to start a program capable of outputting text, as well as the actual code that outputs that text. It's also the chance to get the build tools setup, people forget that some of the 'boilerplate' is the actions required to build, which often are much more involved for newer tools and frameworks.
You can just say initially e.g. when I learned C++ that "#include <iostream>" can be "mostly ignored for now, but just know iostream stands for Input / Output stream, and this line is necessary to e.g. output with std::cout"; and there are no scars left from this.
Some scars... as cout << was always a bad idea, and taught people early on in their C++ development that being overly clever with operator overloading was expected
C# has had top level statements since version 9.0 (Nov 2020), and it's still just a compiler trick that produces a static method behind the scenes. Top level functions work too, but in a similar way.
There are several valuable compiler transformations that happen under the hood in languages like this. Closures as types, iterator/generator functions, async state machines. This is just another example.
> If you can't have other top-level functions in Java, then it's a special case, which is ugly.
I assumed this meant that you could have free functions, but you're saying you can ONLY have `main` as a free function? Ok, then I agree this is also garbage.
I really hated that change when I came out. I'm still not a big fan, but I made my peace with it understand that it benefits the poor users who have to deal with programs whose termination statuses are pseudo-random. Especially users doing scripting.
I write "return 0" at the end of main in all my programs if that end is reachable.
What’s with Java devs and not including `import` lines in example code? Even here, when intending to demonstrate how dumb Java’s boilerplate is. But I notice it everywhere.
Because usually you have an ide that manages imports for you automatically. So unless you are using a library that has multiple imports, usually you know the import to include, and in almost all cases it is automatically included.
Btw: in the compact example! The IO class is implicitly imported, so that example truly works without any import at all!
I mean, the IDE can totally write out the import statements for you as you type and choose resolutions for identifiers; but if you paste in a random code snippet that references ambiguous identifiers from a third-party lib, no IDE can magically generate the import statements required to discriminate the correct resolutions. You have to go through and resolve the symbols yourself (or advance through them as error sites to get the class to compile.)
Or, to put that another way: import statements aren’t pure boilerplate that should be thrown away; they encode real information (ambiguous symbol resolutions); and so, by eliding them from code examples, you’re discarding that information and forcing the learner to re-derive it.
Which is especially bad when your code examples are referencing ambiguous undocumented static inner classes nested deep inside your library’s package namespace, where all the potential resolutions differ only by the particular package they exist in (since they’re all e.g. essentially batteries-included extensions of the base library implementing the same interface.) And where all this structure is as ugly as it is precisely because you didn’t expect the end-user of the library to need to understand it / interact with it. Yet your own [usually “advanced” or “feature demonstrating”] code examples force exactly that.
I learned Java only after Python and I remember being not quite familiar with types so even the 'void' and 'String[]' where a bit mysterious. After learning basic types that part made sense. Then you start learning about Classes and objects, and you understood that main is a static method of this class Main that must be required somehow. As you dive deeper you start learning when this class is called. In a weird way what started as complete, unknowable boiler plate slowly evolved into something sensible as you began to understand the language better. I have no doubt that seasoned Java devs see a lot more in that invocation that I do.
Good riddance though!
1. Programmer A creates a class because they need to do create an entry point, a callback, an interface... basically anything since everything requires a class. Result: we have an class.
2. Programmer B sees a class and carelessly adds instance variables, turning the whole thing mutable. Result: we have an imperative ball of mud.
3. Another programmer adds implementation inheritance for code reuse (because instance variables made factoring out common code into a function impossible without refactoring to turn instance variables from step 2 into arguments). Result: we have an imperative ball of mud and a nightmare of arbitrary dynamic dispatch.
At some point reference cycles arise and grandchild objects hold references to their grandparents in order to produce... some flat dictionary later sent over the wire.
4. As more work is done over that bit of code, the situation only worsens. Refactoring is costly and tedious, so it doesn’t happen. Misery continues until code is removed, typically because it tends to accumulate inefficiencies around itself, forcing a rewrite.
While the teaching is partially to blame, i say it is more that most people are sloppy and undisciplined thinkers. When they dont have any incentive to produce disciplined code, they wont.
As much as C++ has a lot of problems, them and other languages (python/ruby/etc) never denied that the procedural world existed, while Java wants to blindfold you and push you through a corridor until you get out of it and into the "perfect (not) OOP world"
If you're speaking of `__main()`-style entry point, though, you're right, and Java makes it... complicated.
Interestingly, those two languages use very different mechanisms for top-level functions - although I’m not sure if there’s a significant difference in practice.
Python has true standalone functions, whereas in Ruby’s they’re really methods of a `main` object.
And yeah, it was pretty cool looking back on it later and realising I knew what it all meant!
And yeah, good riddance nonetheless!
The answer is: infinitely much. You could have to write each character in strings as separate characters for example. It would be absolute utter madness but it "would make sense later".
(I somehow mostly avoided learning Java, and my last passing contact with it was 20+ years ago, so these are honest questions).
The main function can be written with `String[] args` if you need it.
This is a decent summary of the changes:
https://www.happycoders.eu/java/main-method/
Further details:
https://openjdk.org/jeps/512
Thus, many sub-optimal project coordination approaches ended up fighting the design patterns. Java is a good student language, but there are so many better options now... with fewer footguns, and without Oracles farts. =3
Pulling the oracle card when java is mentioned is a useless stunt.
As much as I love hating Oracle, they pushed the language forward much more than Sun ever did.
How does it affect you this change day to day?
But yeah I don't understand why the author is so excited about this. How "ugly" a trivial "Hello, World!" does not really matter much and isn't a good indication of anything about the language's ability to handle more than "Hello, World!"
Java (historically) is famous for going a bit too hard core into overly abstract & verboseobject oriented design patterns - create a factory to get another factory to get a different factory and whatnot. The hello world is where java shows that style of code is what historical java felt was the ideal.
Sure, but for beginner programmers who don't have the discipline down yet, it's unnecessarily hard. I bought a Java programming book as a kid and got stuck because of a typo that produced an error message I couldn't understand. This was the time before StackOverflow and Reddit. In retrospect, this delayed my programming journey by at least a year.
Longer Hello Worlds make frustration and getting stuck like this more likely.
I used to teach Java, and the total sum of magic incantations you needed before starting to actually "Hello, world" were an instant turn off for many students.
Now that I think of it I rarely even write a truly new class from scratch, usually you just implement/extend some framework-specific thing.
The scanner class is one such thing, but you also have things like wrapping a Reader in a BufferedReader to add buffering, and building a Pattern object to build a Matcher object which has a few different methods you can call to actually do anything useful
It is very OO, but it's also a bit annoying, and more modern java libs tend to give you a more comfortable API. Sadly modern java also tends to come with springboot and people who can't do anything, unless they use springboot
Spring Boot (and Spring generally) is more of a “Java culture” issue — someone on this thread used the term “framework fetishism” and it’s spot on.
Thankfully there are teams, typically in mobile or polyglot microservices environments, who are moving away from this. But yeah — still far too common.
Released in Java 21
https://openjdk.org/jeps/445
It would make Java a much better language in my eyes if it allowed package-level methods, or even multiple records/classes per file. But apparently "impact on how Java code is written in general" is not wanted.
For the longest time Java had been very keen to be explicit, to the point of needing a log of cruft where it just felt like the compiler was checking you spelt every letter of the incantation correctly because both the compiler and you knew unambiguously what simple thing you were trying to achieve. But this tone changed in Java around the time the new entrants like Kotlin started taking over as what you develop in in a way that scala and clojure never managed. And suddenly all the get-out-of-your-way of lambdas saving you typing out classes and anonymous classes arrived and Java became a much nicer experience imo.
No reason apart from the framework epidemic that's supposed to make things easier for developers, but in the end hamstrings them and weighs everything down.
(to the downvoters - I am a java developer and this is very real to me at the moment!)
On my university no one got Sun's marketing money in 1998, yet the distributed systems, compilers design, and graphics programming, all adopted Java as the language for new teaching materials, as it sorted out several problem with the assignments.
Also of course the misuse and overuse of classes when designing systems, but the complexity at design time is also the counterpart of the ease of use of well designed APIs. I remember using an IDE to write some code and it skipped the whole documentation and run/compile error phase, I could just cast mismatching types as needed.
Probably C# and Swift/Objective C would only be above Java as top languages, being actually funded by paying customers instead of used by companies that depend on volunteer developed open source software and end-users that equate paying for software with evilness.
Deleted Comment
For a very long time it completely stagnated and all the features we see added now should've been there 10 years ago, at least, and a bunch of other improvements on top. All because "our goal isn't to adopt the strategy of less successful products, but to forge our own" which now turned into busily adopting all the features that the "less successful products" have had for years: https://news.ycombinator.com/item?id=28985688
Deleted Comment
As a java guy and think python is weird, I don't think this sucks.
But, I also agree that can serve as terrible intro to programming if you start programming right away without understanding the basics of abstractions. But, often when we have tools either designed for a purpose in mind or a dominant paridigm or reaction to existing set of tooling, this can result in understandable yet extreme abstractions.
Java is designed with OOP in mind and it kind of makes sense to have the user to think in terms of lego blocks of interfaces. Every method or class needs to have clear understanding of its users.
public - software handle is for all users
protected - software handle for current and extending classes
default - software is exposed to current package
private - software is restricted to be used in current class alone and nowhere else
So, the beginning of java programming starts with interface exposed to the user or other programmers. Is it weird and extreme. Yes. At least, it is consistent.
Java choose to adopt object oriented purity in a very weird way. Sun has initially rejected the idea of standalone functions (static import was introduced in Java 5, closures in Java 8 and compact source files/instance main is only coming now). Even with static imports and compact source files, there is still a class holding these methods behind the scenes (for practical and compatibility reasons, more than to achieve any ideological or theoretical goals at this point). That seems like Sun was trying to keep the purity of "everything is an object", but at the same time they've introduced primitive values, which are clearly not objects. This was a pretty bad decision in my opinion. There is no practical benefit from requiring every function to be defined inside a class[1]. On the other hand, being able to have methods defined on an integer is a pretty nice feature.
If we look at a Smalltalk, which is generally considered be an example of a "pure" OOP language, we see a striking difference. This is Hello World in Smalltalk:
Smalltalk allows you to freely define functions outside of classes and even write code that is not part of a class (directly in the REPL).[1] https://steve-yegge.blogspot.com/2006/03/execution-in-kingdo...
We even have design patterns like Command, to workaround first class functions in "pure" OOPy way.
And for enterprise software development, I like it that way. It can make up a definition it wants and stick to it. I think it is better for a language's ecosystem and culture to have one dominant paradigm than becoming kitchen sink of programming languages.
Edit: added a link
[1] softwareengineering.stackexchange.com/questions/264697/alan-kay-the-big-idea-is-messaging
Please show an example.
You can just say initially e.g. when I learned C++ that "#include <iostream>" can be "mostly ignored for now, but just know iostream stands for Input / Output stream, and this line is necessary to e.g. output with std::cout"; and there are no scars left from this.
If you can't have other top-level functions in Java, then it's a special case, which is ugly.
Decompiled example: https://lab.razor.fyi/#41rAyMUVUJSfXpSYq5dcLDSRsbQ4My9dIbiyu...
> lipstick on a pig's turd
There are several valuable compiler transformations that happen under the hood in languages like this. Closures as types, iterator/generator functions, async state machines. This is just another example.
I assumed this meant that you could have free functions, but you're saying you can ONLY have `main` as a free function? Ok, then I agree this is also garbage.
```Top-level members are interpreted as members of the unnamed class, so we can also write the program as:
String greeting() { return "Hello, World!"; }
void main() { System.out.println(greeting()); }```
I write "return 0" at the end of main in all my programs if that end is reachable.
Btw: in the compact example! The IO class is implicitly imported, so that example truly works without any import at all!
Or, to put that another way: import statements aren’t pure boilerplate that should be thrown away; they encode real information (ambiguous symbol resolutions); and so, by eliding them from code examples, you’re discarding that information and forcing the learner to re-derive it.
Which is especially bad when your code examples are referencing ambiguous undocumented static inner classes nested deep inside your library’s package namespace, where all the potential resolutions differ only by the particular package they exist in (since they’re all e.g. essentially batteries-included extensions of the base library implementing the same interface.) And where all this structure is as ugly as it is precisely because you didn’t expect the end-user of the library to need to understand it / interact with it. Yet your own [usually “advanced” or “feature demonstrating”] code examples force exactly that.