> Worse, the early exposure to static methods will turn out to be a bad habit that must be later unlearned.
I have been using Java since 1.0 and it is my default language. I am writing Java code today.
I write functions using ‘static’ all the time. I prefer that a function be static. It shows that it does not depend on state of the enclosing object. Using ‘static’ on methods is not a bad habit. Having static fields and state is to be avoided.
It's worth keeping in mind that the primary audience that the article discusses is not folks at your skill level.
By far one of the most common errors I see as a CS instructor is aggressive over-reliance on global state. Not global methods, but state. However, usually what causes the student to reach for global state is the fact that it can be accessed from a static method like main; In lieu of figuring out how to structure a class, they reach for the easier "just make the variable static" approach. Allowing the initialization sequence to call main on an instance via the class's zero-arg constructor seems like it would address the point.
Recently I tried another take on the same idea: using default methods in Interfaces. It has nice properties, as it enables ~mixins in a way not dissimilar to what Scala provides, and enables overriding (e.g. for tests). Give it a try, it's ... refreshing :)
Static methods are a pain for unit testing though. I have started avoiding them for that reason alone. For stateless objects, you can always have a singleton and invoke all the methods on that object giving effectively static behavior without the baggage.
> It shows that it does not depend on state of the enclosing object.
Fine, but that's not relevant to the subject of this document, the "first program", as it would make all fields static, too, and so methods would still be dependent on state. It will just make moving to the next step harder; as the article says: this is probably not the direction we want to go when we scale up from a handful of statements and declarations to a simple class — we probably want to start using classes as classes, not just as containers for static members.
They’re fine if they are self contained, which is your point, and which is the author’s point (and generally everyone else). He didn’t go into detail of the why, but that is east is usually meant.
A good clean programming have it is to wrap all of your stream()/lambdas chains in a static method that describes what they do. This make sure they don’t have access to the local scope and can mutate data.
static should be removed from Java. static is bad, not always but because it can be misused and because it makes debugging more difficult and most important of all, it makes testing impossible and PowerMock is to be avoided at all costs.
I always love seeing Brian Goetz’s articles, they explain the problem succinctly and they do a great job of validating their approach, showing the shortcomings of alternatives.
I especially like the language team’s very conservative attitude, valuing backwards compatibility greatly. Here, they again solve most of the problem by simply changing the “program launcher” code, without any modifications/special casing done on the language level. For the rest, we will see. I think foregoing public requirements is a no-brainer, the rest may need a bit further thought based on further comments, here is the mailing list: https://mail.openjdk.org/pipermail/amber-spec-observers/2022...
Succinctly? You must be a Java programmer. This essay is at least 3-4x longer than I’d like.
As a (mostly) python and C++ programmer, Java feels like someone is afraid they’re speaking to an imbecile, so they constantly repeat themselves and over-explain every point.
Applause! However, it's not always about "saving keystrokes"... it's about lowering accidental complexity and making code easier to reason about by requiring less cognitive load on the reader.
Another example: the lack of literals for collections in Java. Imagine this:
Map x = {"a": "A", "b": "B"}; // not Java
vs:
Map x = new java.util.HashMap();
x.put("a", "A");
x.put("b", "B");
The first case is more declarative, and thus easier to grasp.
In the second case you have to read, parse and "execute" line by line in your head... (Does ordering matter? Does line 3 depend on line 2?, etc).
When things like these compound, you end up with code-bases many times bigger and harder to grasp than in other languages.
Granted: you can write shitty code in any language, nothing will save you from that... But I think Java makes it harder to write simple and concise code, even to experienced coders.
Then there's also the "cultural" thing many already mentioned ("enterprise Java")... which is very real, but no JEP/JSR will fix that ;-).
Brian shows brilliance, even at "trivial" issues. I trust his judgement :-).
Java is deliberately nudging you not to do things like that. It would rather you create a class with fields a and b -- which has its own verbosity, though they've improved it.
I find that I use structures like that primarily when interfacing with other loosely-typed languages, like Javascript, which do encourage you to create structures on the fly like that. Impedance mismatch is always a pain, no matter where it occurs.
That's not to say Java doesn't want you to use Maps. They do -- but only when the content is dynamic. They don't want to add literals for it because that's the opposite of their goal.
There are days when you want it anyway, and then you grumble that the language has forced you to do something ugly. But every language is a set of tradeoffs where some things are ugly, other things are pretty, and the language spec doesn't require a shelf of its own.
I like the idea of simplifying the main method, but it's honestly not a huge barrier as a high school teacher. Most IDEs will fill in the declaration of main for you, and I think it's perfectly fine to tell students "we'll worry about that later, write your code in the braces" without diving in to all the details. To me, what would be more helpful is an easier way to read input from stdin during a program. I usually give my intro students a static class that wraps Scanner, but it's a bit of a pain.
When i read the title, I thought they were finally going to fix gradle or improve maven. Those are the real on-ramps and they are a much bigger barrier to entry than main().
A cleaner/simpler alternative was Kobalt, but it’s now abandoned. A simple, official build tool would make the ecosystem easier to learn.
There are a bunch of different problems better described as "off-cliffs" than on-ramps; initial syntax / conceptual complexity is one, build systems are another. Not that other ecosystems are necessarily better. Modern web dev is pretty damn confusing for beginners as well. I helped an ex-girlfriend as she went through a coding bootcamp. Very little of the material stuck and it wasn't her fault. The whole stack is just far too complicated.
The biggest issue for teaching Java is IMHO not really the syntax of hello world - as Brian says, teachers can handwave this by saying they'll explain it later. It's worth improving mostly because it helps regular programmers who want to use Java in a scripting-like way. The more fundamental difficulty that newbies have is showing other people what they've done.
In the old JRE days it was easy - just send them a fat JAR that opens up a window with Swing and does some cool stuff. Your friends/parents/teachers/etc would probably have a JRE installed already and it'd all just work. In recent years they've abandoned the idea of a JRE and JARs as an app format. You can still open up a window with Swing or JavaFX and get scribbling, but now to show anyone what you did they want you to bundle the JVM and make a more 'native' style app with it. There's a lot to recommend that approach overall, but it's unquestionably harder for learners.
Last year I was discussing this problem with Dmitry Jemerov (product manager for IntelliJ). He sees this issue directly - learners download IntelliJ, get some cool pixels on screen with Swing or JavaFX and then hit a brick wall when they want to send their program to other people. They ask around and it becomes "oh why aren't you learning web dev?" and then they balk when they realize that involves learning 3 new languages and maybe server sysadmin stuff. App stores don't help here because Apple/MS don't like their nice discovery system being gunked up with "my first code" stuff. Even if they stick with Java and try to learn jpackage they quickly realize they need Windows, and a Mac, and maybe Linux just to make packages that their friends can run at all, and then having done all that there's no way to push updates!
We were discussing this problem in the context of maybe doing integrating IntelliJ together with Conveyor [1], a tool my company has been writing that makes desktop app distribution drastically simpler. Actually if you are OK with self-signing it's no harder than compiling Markdown to HTML is, the workflow is very similar. Even if you want to sign it's still very easy; the hardest part is just the general bureaucracy involved with buying the certs. It works for any kind of app including Electron or native apps, but of course with a JVM app you don't have to cross-build anything and Java GUI toolkits are a bit easier to learn than web dev.
There's more work to do here, for instance drawing icons is tedious and when learning a distraction, so we've got a feature in development that draws icons for you. And whilst you can upload the resulting package repository to GitHub Releases, that's not automated and it could be.
I think we're pretty close now to basically abolishing this on-ramp/off-cliff, at least if your users are willing to copy/paste commands to the terminal (due to the self-signing; if you supply real code signing certs the UX is obviously much nicer). Getting rid of the code signing requirements means sandboxing but that's a bigger lift.
BTW - Conveyor is actually implemented as a parallel incremental task-caching build system, internally. We've generalized it and are currently experimenting with that for some of our own build needs. We're some way from being able to replace Maven/Gradle but totally agree that usability of existing build tools is very poor. Usability is in fact the primary ground on which this experimental build system differentiates.
Confused about the criticisms now. Otherwise everybody complains about Java boilerplate. Remove some of it and make it optional, to avoid bogging down newbies in irrelevant details in the first instance, and further lead to less boilerplate to make simpler applications in the second instance (or further down the line). I'm not seeing a massive amount of effort being expended here, and I don't see how it's being wasted.
When you have such a large ecosystem (Java's user base is larger than the population of quite a few European countries or the population of the San Francisco Bay Area), no matter what you do or don't do, there will be plenty of complaints because there are few things programmers are in consensus over. Satisfying everyone is impossible. If 99% are content with some Java feature (or lack thereof) and 1% of the remaining 1% complain online, you're looking at a thousand complainers.
> Otherwise everybody complains about Java boilerplate.
Do serious people actually complain about this often? I think it's more of a drive-by comment from people who don't like Java for whatever other reasons.
I agree. Ironically, Java the language doesn't have any more boilerplate than, say, C++ or Go, which people are apparently okay with?
It does however have some stuff that's plain dumb (can't have functions outside classes, public static void main(String[]), ...) some of it isn't even fixable (default visibility should be private, final should be like const in C++, or even better like const in D).
This is a change that takes very little effort and doesn't impact professional development in the slightest.
Instantly my mind wants to "javaify" it again, that is, generalizing it to some general case by involving qualified names:
Instead of stubbornly implementing Runnable from those flat files, mix in the well-established concept of single-function-interfaces (Runnable is just one of many) and introduce a top-level "extends" that makes the file body an SFI implementation:
Not a Java dev here, at least in part because the amount of ceremony to do trivial things seems excessive.
My understanding is that the ceremony and redundancy is deliberate. You type the same meaning multiple times so the compiler can detect inconsistencies and tell you about them. I.e. it's a feature, not a bug. Slightly analogous to type annotations or unit tests, one wants to write the redundant information in exchange for compiler diagnostics.
Syntactic sugar to make the language less annoying to work in is opposed to that goal, hard to guess if that's better for the target market.
This seems to be a weird micro-optimisation. While I agree with some of the goals (classless files, parameterless main exist in Kotlin and are nice to have), they should, in my opinion, not be implemented for the sole purpose of making `main` as minimal as possible.
I don't think you need to understand every single character of your very first program right from the beginning. A few of the concepts can be hand waved away to be explained in a later chapter without impeding the overall understanding of programs once you left the sub-twenty-lines beginner programs.
Extra language specification rules that you need to be aware of for your whole career... in order to make the first 30 seconds easier when looking at hello-world for the first time.
C# has this on their new ASPNET 7.0 docs. To have a single Program.cs file with all the builder and app code . incredibly accessible to a new learner of the framework.
This is still too long. I think C# already supports top-level execution (eg, your program is assumed to be main() as do almost all other languages). `println` is also a bad name for something that could just be `log` or `print`.
I’m actually very happy to see this after using Java for many years. I definitely had all of the mentioned thoughts about the keywords, and not knowing what they do when first learning.
Java needs staunchly maintain its static type nature rather than move towards a ‘everything is a string/array whatever’. The concept of types are not only powerful, performant, but they are way to grasp conceptually because the Java semantics and compile time checking.
I do wish CDI (or SpringFramewotk) was brought into CS and similar degrees. Dependency Injection brings a large number of good habits to developers, but it is a concept that is hard to teach as an employer. In our onboarding program, we have to teach people about the CDI container (a program ruining your program) managing all of your state for you (to great benefit and speed of development). During this time, we’re not getting much value out of the developer, and the teacher is also away from their normal duties as well. :/
The declaration of a class and the incantation of public static void main is pure mystery to a beginning programmer.
This article misses the point, however. The problem is not writing your first hello world. The ceremony is not so much in the language itself but in the infrastructure. The verbosity of the language is just the tip of the iceberg.
I think Java back in the 90's started out actually quite decently, although from the get-go it had the verbosity. But at least it didn't have everything else yet that you're expected to know for any non-trivial program.
The first time the word "enterprise" appeared together with Java was the moment it went all downhill.
Sounds like great improvements. I would wish for the ramp to be extended a little bit to also do something about packages, for example allowing a common prefix like "com.company.project.whatever" to be elided, so that we don't have to go from "src/Hello.java" straight to navigating the maze of "src/main/java/com/company/project/whatever/hello/Hello.java".
I have long considered that having to have the class definition explicit in the file is redundant, especially as the 2 have to have the same name anyway. Obviously if you need to `extend` or `implement` then an explicit declaration is needed. But otherwise, just defaulting to the file base name is more than sufficient.
I remember when first learning Java in 1995, and already being familiar with OOP from C++, I found it curious that such an OOP-focused language didn’t represent a program 1:1 as an object instantiated and initialized at load time, with a main/run method invoked on it to start execution. The choice of having main() be static was made even weirder by its still having to be defined within a class. Then if you’d want to have your application be an object, you’d have to do the extra `new MyApp(args).run()` bit manually, and have run() be the “real” main method. It all seemed very backwards.
I wonder - why he has not take it a step further and propose to get rid of return type (void) also? It would look even more slick (add optional semicolons and we will get javascript out of this :)).
main() {
println("Hello World");
}
I know that this notation is traditionally reserved for constructors but I'm not 100% sure why we cannot differentiate between methods and constructors just by looking at class name?
I see very little gain for quite a high price here. It would break the grammar quite a bit, potentially causing even breaking changes, for something that is usually not absent even in languages that do strong type inference.
The proposed changes don’t have these negatives, while still provide benefits.
Doesn't Groovy (https://groovy-lang.org) achieve much of this? I remember being taught with it at university for some time before they introduced Java. With Groovy you don't need the class or main method, and can have a program which is just `println "Hello world!"`.
One additional comment: they need to make the launch protocol for jar files, much simpler as well. META-INF needs to be replaced with a simple properties file that indicates what method to run and which libraries to have on the class path.
There's a new version of Head First Java that came out this year, I expect it's reasonably up to date wrt modern language features. I haven't read it personally but it's frequently recommended.
At a more intermediate level Effective Java 3rd edition is a must read.
Kotlin's syntax is cleaner in some most places, more explicit in some places (no implicit nulls, and wrt generics) and much terser when using naked functions etc.
I always find it funny when people bring up Kotlin in the context of Java's evolution. It's not one of Java's main competitors, and while Java has borrowed many features from other languages [1], to date it has not adopted a single feature from Kotlin [2]. In fact, almost at every turn -- from data classes, through async/await, to string interpolation (JEP 430) -- Java has opted for a different approach. Is Kotlin the only other language they know?
But I don't think you're entirely wrong to suggest that competition had something to do with this. Python is one of Java's main competitors, and when it comes to teaching a first language it is the main competitor. Both students and teachers who prefer Python as a first language mentioned this "on-ramp" problem as one of the reasons. There are others, and they will be addressed by other enhancements.
[1]: Java's "charter" calls for it being a last mover, adopting features after they've been successfully used elsewhere
[2]: Although there is one I hope it would one day -- nullability types.
mcculley|3 years ago
> Worse, the early exposure to static methods will turn out to be a bad habit that must be later unlearned.
I have been using Java since 1.0 and it is my default language. I am writing Java code today.
I write functions using ‘static’ all the time. I prefer that a function be static. It shows that it does not depend on state of the enclosing object. Using ‘static’ on methods is not a bad habit. Having static fields and state is to be avoided.
okennedy|3 years ago
By far one of the most common errors I see as a CS instructor is aggressive over-reliance on global state. Not global methods, but state. However, usually what causes the student to reach for global state is the fact that it can be accessed from a static method like main; In lieu of figuring out how to structure a class, they reach for the easier "just make the variable static" approach. Allowing the initialization sequence to call main on an instance via the class's zero-arg constructor seems like it would address the point.
StevePerkins|3 years ago
Noe2097|3 years ago
Recently I tried another take on the same idea: using default methods in Interfaces. It has nice properties, as it enables ~mixins in a way not dissimilar to what Scala provides, and enables overriding (e.g. for tests). Give it a try, it's ... refreshing :)
sz4kerto|3 years ago
monster_group|3 years ago
pron|3 years ago
Fine, but that's not relevant to the subject of this document, the "first program", as it would make all fields static, too, and so methods would still be dependent on state. It will just make moving to the next step harder; as the article says: this is probably not the direction we want to go when we scale up from a handful of statements and declarations to a simple class — we probably want to start using classes as classes, not just as containers for static members.
exabrial|3 years ago
A good clean programming have it is to wrap all of your stream()/lambdas chains in a static method that describes what they do. This make sure they don’t have access to the local scope and can mutate data.
thedracle|3 years ago
I'm not really sure what they're on about.
salil999|3 years ago
This is totally false. It will always depend on the use case.
RockingGoodNite|3 years ago
kaba0|3 years ago
I especially like the language team’s very conservative attitude, valuing backwards compatibility greatly. Here, they again solve most of the problem by simply changing the “program launcher” code, without any modifications/special casing done on the language level. For the rest, we will see. I think foregoing public requirements is a no-brainer, the rest may need a bit further thought based on further comments, here is the mailing list: https://mail.openjdk.org/pipermail/amber-spec-observers/2022...
RayVR|3 years ago
As a (mostly) python and C++ programmer, Java feels like someone is afraid they’re speaking to an imbecile, so they constantly repeat themselves and over-explain every point.
ferd|3 years ago
Another example: the lack of literals for collections in Java. Imagine this:
vs: The first case is more declarative, and thus easier to grasp. In the second case you have to read, parse and "execute" line by line in your head... (Does ordering matter? Does line 3 depend on line 2?, etc).When things like these compound, you end up with code-bases many times bigger and harder to grasp than in other languages.
Granted: you can write shitty code in any language, nothing will save you from that... But I think Java makes it harder to write simple and concise code, even to experienced coders.
Then there's also the "cultural" thing many already mentioned ("enterprise Java")... which is very real, but no JEP/JSR will fix that ;-).
Brian shows brilliance, even at "trivial" issues. I trust his judgement :-).
kubota|3 years ago
jfengel|3 years ago
I find that I use structures like that primarily when interfacing with other loosely-typed languages, like Javascript, which do encourage you to create structures on the fly like that. Impedance mismatch is always a pain, no matter where it occurs.
That's not to say Java doesn't want you to use Maps. They do -- but only when the content is dynamic. They don't want to add literals for it because that's the opposite of their goal.
There are days when you want it anyway, and then you grumble that the language has forced you to do something ugly. But every language is a set of tradeoffs where some things are ugly, other things are pretty, and the language spec doesn't require a shelf of its own.
csmeyer|3 years ago
hddqsb|3 years ago
_ea1k|3 years ago
pabl0rg|3 years ago
A cleaner/simpler alternative was Kobalt, but it’s now abandoned. A simple, official build tool would make the ecosystem easier to learn.
https://github.com/cbeust/kobalt
sideeffffect|3 years ago
mike_hearn|3 years ago
The biggest issue for teaching Java is IMHO not really the syntax of hello world - as Brian says, teachers can handwave this by saying they'll explain it later. It's worth improving mostly because it helps regular programmers who want to use Java in a scripting-like way. The more fundamental difficulty that newbies have is showing other people what they've done.
In the old JRE days it was easy - just send them a fat JAR that opens up a window with Swing and does some cool stuff. Your friends/parents/teachers/etc would probably have a JRE installed already and it'd all just work. In recent years they've abandoned the idea of a JRE and JARs as an app format. You can still open up a window with Swing or JavaFX and get scribbling, but now to show anyone what you did they want you to bundle the JVM and make a more 'native' style app with it. There's a lot to recommend that approach overall, but it's unquestionably harder for learners.
Last year I was discussing this problem with Dmitry Jemerov (product manager for IntelliJ). He sees this issue directly - learners download IntelliJ, get some cool pixels on screen with Swing or JavaFX and then hit a brick wall when they want to send their program to other people. They ask around and it becomes "oh why aren't you learning web dev?" and then they balk when they realize that involves learning 3 new languages and maybe server sysadmin stuff. App stores don't help here because Apple/MS don't like their nice discovery system being gunked up with "my first code" stuff. Even if they stick with Java and try to learn jpackage they quickly realize they need Windows, and a Mac, and maybe Linux just to make packages that their friends can run at all, and then having done all that there's no way to push updates!
We were discussing this problem in the context of maybe doing integrating IntelliJ together with Conveyor [1], a tool my company has been writing that makes desktop app distribution drastically simpler. Actually if you are OK with self-signing it's no harder than compiling Markdown to HTML is, the workflow is very similar. Even if you want to sign it's still very easy; the hardest part is just the general bureaucracy involved with buying the certs. It works for any kind of app including Electron or native apps, but of course with a JVM app you don't have to cross-build anything and Java GUI toolkits are a bit easier to learn than web dev.
There's more work to do here, for instance drawing icons is tedious and when learning a distraction, so we've got a feature in development that draws icons for you. And whilst you can upload the resulting package repository to GitHub Releases, that's not automated and it could be.
I think we're pretty close now to basically abolishing this on-ramp/off-cliff, at least if your users are willing to copy/paste commands to the terminal (due to the self-signing; if you supply real code signing certs the UX is obviously much nicer). Getting rid of the code signing requirements means sandboxing but that's a bigger lift.
BTW - Conveyor is actually implemented as a parallel incremental task-caching build system, internally. We've generalized it and are currently experimenting with that for some of our own build needs. We're some way from being able to replace Maven/Gradle but totally agree that usability of existing build tools is very poor. Usability is in fact the primary ground on which this experimental build system differentiates.
[1] https://conveyor.hydraulic.dev/
Sakos|3 years ago
pron|3 years ago
chrisseaton|3 years ago
Do serious people actually complain about this often? I think it's more of a drive-by comment from people who don't like Java for whatever other reasons.
qsort|3 years ago
It does however have some stuff that's plain dumb (can't have functions outside classes, public static void main(String[]), ...) some of it isn't even fixable (default visibility should be private, final should be like const in C++, or even better like const in D).
This is a change that takes very little effort and doesn't impact professional development in the slightest.
joshka|3 years ago
E.g. Foo.java
Becomes: Then running java Foo.java instead just stubs a main() that looks like:usrusr|3 years ago
Instead of stubbornly implementing Runnable from those flat files, mix in the well-established concept of single-function-interfaces (Runnable is just one of many) and introduce a top-level "extends" that makes the file body an SFI implementation:
(and it only gets better if you also add the rule "in absence of any imports statements, an implicit import java.lang.*; is added)ajnin|3 years ago
JonChesterfield|3 years ago
My understanding is that the ceremony and redundancy is deliberate. You type the same meaning multiple times so the compiler can detect inconsistencies and tell you about them. I.e. it's a feature, not a bug. Slightly analogous to type annotations or unit tests, one wants to write the redundant information in exchange for compiler diagnostics.
Syntactic sugar to make the language less annoying to work in is opposed to that goal, hard to guess if that's better for the target market.
Kolja|3 years ago
I don't think you need to understand every single character of your very first program right from the beginning. A few of the concepts can be hand waved away to be explained in a later chapter without impeding the overall understanding of programs once you left the sub-twenty-lines beginner programs.
chrisseaton|3 years ago
pjmlp|3 years ago
abledon|3 years ago
nailer|3 years ago
exabrial|3 years ago
Java needs staunchly maintain its static type nature rather than move towards a ‘everything is a string/array whatever’. The concept of types are not only powerful, performant, but they are way to grasp conceptually because the Java semantics and compile time checking.
I do wish CDI (or SpringFramewotk) was brought into CS and similar degrees. Dependency Injection brings a large number of good habits to developers, but it is a concept that is hard to teach as an employer. In our onboarding program, we have to teach people about the CDI container (a program ruining your program) managing all of your state for you (to great benefit and speed of development). During this time, we’re not getting much value out of the developer, and the teacher is also away from their normal duties as well. :/
kleiba|3 years ago
This article misses the point, however. The problem is not writing your first hello world. The ceremony is not so much in the language itself but in the infrastructure. The verbosity of the language is just the tip of the iceberg.
I think Java back in the 90's started out actually quite decently, although from the get-go it had the verbosity. But at least it didn't have everything else yet that you're expected to know for any non-trivial program.
The first time the word "enterprise" appeared together with Java was the moment it went all downhill.
kaba0|3 years ago
ptx|3 years ago
jodastephen|3 years ago
My concept is much more powerful by having a new kind of class declaration alongside 'record' and 'enum' - 'entrypoint'.
kitd|3 years ago
layer8|3 years ago
monkpit|3 years ago
Edit: but a bad job at a lot of other things.
layer8|3 years ago
v-erne|3 years ago
main() { println("Hello World"); }
I know that this notation is traditionally reserved for constructors but I'm not 100% sure why we cannot differentiate between methods and constructors just by looking at class name?
kaba0|3 years ago
The proposed changes don’t have these negatives, while still provide benefits.
hughjd|3 years ago
chrisseaton|3 years ago
Well no, because that on-ramps you to Groovy, not Java.
aaa_aaa|3 years ago
AtlasBarfed|3 years ago
exabrial|3 years ago
brnt|3 years ago
absove|3 years ago
At a more intermediate level Effective Java 3rd edition is a must read.
cies|3 years ago
Did Java's papa get jealous?
pron|3 years ago
But I don't think you're entirely wrong to suggest that competition had something to do with this. Python is one of Java's main competitors, and when it comes to teaching a first language it is the main competitor. Both students and teachers who prefer Python as a first language mentioned this "on-ramp" problem as one of the reasons. There are others, and they will be addressed by other enhancements.
[1]: Java's "charter" calls for it being a last mover, adopting features after they've been successfully used elsewhere
[2]: Although there is one I hope it would one day -- nullability types.