JEP 445: Flexible Main Methods and Anonymous Main Classes (Preview)
openjdk.org
openjdk.org
Getting your "first" program off the ground quickly is good, but only if it doesn't cripple your understanding of what's going on for months later.
For me the main thing that confuses the crap out of students is how people who introduce java tend to take "shortcuts" and bundle all functionality under the sun into a single class, which then also has a main method at the end. Presumably this is done so that everything is a single file, or can fit in a single presentation slide. But then suddenly your main looks like it's calling its container class to make objects of "itself" that contain new "mains", at which point your student has lost the plot completely.
The simplest thing to keep things clean and self explanatory is to separate everything that isn't "main-related" into its own class (preferably another file if public), and then keep a nice clean public class called "Main" which contains 'only' the main method. Then you get to explain nicely how public classes have their own files, and if you attempt to run this class from the terminal, java will look for the 'main' function, and how this therefore needs to be public, static because you're running it straight from the class, void because you don't depend on it returning a value (which also makes for a nice intermission on how c returns status as an int, but java has a special mechanism instead).
I do agree with the end of the JEP though; if you want to teach simple snippets like System.out.println, jshell is a nice way to do this, if you want to delay talking about classes and their methods.
Exactly. This is one of the problems this JEP attempts to solve.
> separate everything that isn't "main-related" into its own class ... and then keep a nice clean public class called "Main" which contains 'only' the main method. Then you get to explain nicely how public classes have their own files, and if you attempt to run this class from the terminal, java will look for the 'main' function, and how this therefore needs to be public, static because you're running it straight from the class, void because you don't depend on it returning a value
Only then you've taught a number of programming-in-the-large concepts as well as a bunch of Java technicalities -- all designed for the purpose of building large programs in a team -- before teaching what a computer program is.
However, if you use this feature to teach your students and encounter a problem, we would be interested to hear your experience on the amber-dev mailing list.
I see this as great for developers already familiar with the language in making code more streamlined. But it's along a similar vein to, say, "anonymous classes can be expressed as lambdas which can in turn be expressed as method references". Or, say, the 'var' keyword as a way to automatically infer the variable type and how memory will be allocated for it, on the basis of the applied constructor. In both cases, the last step may be a nice, simple-looking expression, but it hides a lot of underlying complexity and context, which you need to understand properly to use it effectively.
So the part I 'object' to in the wording of the JEP is the educational intent/strategy implied in the summary. For me there is a big difference between something like "introduce complex idea A at a higher level because simpler idea B depends on it", vs "obfuscate the true nature of A to pretend B can be understood in isolation, and deal with the resulting confusion later". Learning works by creating basic blocks of understanding, consolidating them, and then using them to build more complex blocks later on. Relying on a faulty foundation, hoping to come back and re-build it later on is generally not the best educational strategy (in my humble view).
So from an educational point of view, for me this does not make my job simpler. If I wanted my students to build a solid foundation of understanding, I would have to first explain what classes are, what static methods are, how methods can NOT in fact live outside a class in java, and only THEN explain that "to make things less verbose, in this VERY specific case, java allows you some syntactic-sugar to hide some of that complexity under a simpler-looking syntax, that would otherwise look weird in standard java".
But yes, having said all that, this seems like a nice proposal for the language more generally, that could lead to further similar improvements down the line, and I would be in favour of it! Great work!
What confusion? All the code in the body of an anonymous class is perfectly valid Java code that you can put in the body of an anonymous class today. There is no new magic here.
> I would have to first explain what classes are, what static methods are, how methods can NOT in fact live outside a class in java, and only THEN explain that "to make things less verbose, in this VERY specific case, java allows you some syntactic-sugar to hide some of that complexity under a simpler-looking syntax, that would otherwise look weird in standard java".
I don't think you would.
There is no syntactic sugar here that Java doesn't already do. The body of an anonymous main class is valid as the body of an anonymous class today -- there is no special magic or any kind of special interpretation of code here -- and the notion of "implicitly supply an unnamed instance of a programming-in-the-large concept" is something Java already does today for packages and modules.
Every method lives in a class, every class lives in a package, every package lives in a module. And yet, we don't require you to explicitly declare a package or a module. The current way we treat class declarations is the inconsistent one: it requires a class declaration even when it's not useful.
Classes are no more fundamental to how Java "really" works under the covers than modules and class loaders. And yet, Java doesn't force you to learn these concepts before learning what a variable is or what a loop is. There is simply no way to know how Java really works without understanding modules. Modules are what gives actual meaning to access modifiers. Do you teach modules before loops today?
Classes (and so static) and access modifiers really are completely meaningless before you have more than one class or more than one object, just as modules are meaningless until you have several of them. We are making Java more consistent in not an explicit invocation of a concept that is only meaningful once you add more context to it.
I hadn't thought it like that. Sure, that makes sense from that angle.
Putting code in a single file is definitely an asset for instruction, whether Java, C, Python, JavaScript, etc.
That said, a Java file can have multiple classes (though just one public class).
From the questioning around "needs to import basic utility classes", I'm guessing the feature following this would be something like C# implicit usings?
[0] https://devblogs.microsoft.com/dotnet/c-9-0-on-the-record/#t...
IIRC C# doesn't have the complex main selection as in here because it relies on csproj files.
Next step, language changes for Jakarta EE/Spring/Quarkus/Helidon minimal APIs.
But it is easy to “name” it when the program have to grow.
This still seems pretty useless, It'd be nice if java could allow for static interface methods.
interface Program {
static void main(String...args);
}
And then I digressed - heavily.There have been several times when I've wanted static methods that apply to the specific class - although many of the situations could be shoehorned into a constructor, it's not the best.
You'd also want something like a 'This' meta-class keyword.
interface Foo extends Comparable<This> {
static This(A argA, B argB);
static This difference(This v0, This v1);
static int compare(This v0, This v1);
default int compareTo(This other){
return compare(v0, v1);
}
}
/*
I want to return an instance of my class' own type (defined in a subclass an so unknown),
so I pass every implementation itself as its own generic parameter.
*/
interface Why<W extends Why<W>> implements Comparable<W>{
W someManipulation();
}
class Foo implements Why<Foo> {
@Override
Foo someManipulation(){
....
}
}
To be honest, I think I reacted to how robust this does not feel.It also blurs the line between static and instance syntax/functionality in a way that does not seem useful - and given that this is intended to reduce barriers to entry, is quite probably harmfully confusing.
interface Summable {
static This sum(This v0, This v1);
}
interface Subtractable {
static This difference(This v0, This v1);
}
interface Divisible {
static This quotient(This v0, This v1);
}
interface Mulitiplicable {
static This product(This v0, This v1);
}
interface Number extends Summable, Subtractable, Divisible, Multiplicable;
void <N extends Number> doThings(N v0, N v1){
N sum = v0 + v1;
}https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tu...
https://learn.microsoft.com/en-us/dotnet/api/system.numerics...
Another point: IDEs will be able to tell you which main method will get chosen, which should eliminate at least some uncertainty.
Don't forget to support shebang. So we can finally use Java as a system scripting language.
Maybe something like:
#!/usr/bin/env java
And maybe also allow specifying the runtime version: #!/usr/bin/env java-12.0.*When I write the Getting Started for my (FOSS) library, I'll definitely make a JBang variant. (Gitpod and SDKMAN also look interesting.)
The use of Groovy's @Grab et al is also interesting. I'm eager to see how the dependency, imports, modules, local repo cache story, etc story plays out.
Even after all this time, the Java stack still hasn't found its sweet spot. Though I confess I don't know what that'd look like.
#include <stdio.h>
int main() {
printf("Hello world\n");
return 0;
}
But I wish we could get rid of that "boilerplate" #include. I think compilers could add it by themselves when needed (many already suggest it to you if you forget it, so they do know when it's the case).On the other hand I wouldn't get rid of the explicit return from main(), as it has an important pedagogical function.
public class Foo {
int x;
public Foo(int x) {
this.x = x;
}
void main() {
System.out.println(x);
}
}> If a launched class has no static main method but has a non-private zero-parameter constructor ... and a non-private instance main method, then construct an instance of the class... [and] invoke the instance main method
Your class doesn't have a zero-parameter constructor nor a suitable static main, so launching it as a program will fail (as it would today).
Bob: I get this weird error when I run this example from class.
Sally: Works for me! Hey can you try running this?
Bob: oh that works, lots of extra stuff in it. I guess I have to copy all of these things along for an app to work anywhere.
Sally: I guess so - it's weird the simple example works on my laptop and not yours.
Bob: must be a compiler bug, I hear about those alot in the compsci lab.
Personally I think that the compiler should generate a synthetic main entry point when it encounters code that matches the new "launch protocol". It provides a degree of portability and repeatability over relying on the runtime to select the entry point. There are probably downsides here too.
In this setup the generated classes/jars work fine without extra flags to java and even work on older jvm installations if you pass the right flags to javac at build time. I really struggle to come up with a reason to implement this as a change to java vm over just javac.
> Eliminating the main method altogether may seem like the natural next step, but it would work against the goal of gracefully evolving a first Java program to a larger one and would impose some non-obvious restrictions (see below):
The "see below" boils down to there being a number of differences between how class-level and method-level code units (fields vs. variables) work, and blurring the line there isn't worth the minor additional effort of writing a main() method.
println("Hello");
Without even the System.outWill allows for easy experimentation. Once you need to use methods, put it into a static method and a class.
Glad to see them evolving faster as a result
There is negative net benefit to pretty much everyone involved, and for what?
Of course, you can tell students to ignore most of the code until later in the course when they'll learn all about it, but that doesn't work. They'll make sense of the code within their limited understanding of programming and frames of reference. They'll build local theories of how and why these programming elements, like "static", "class", "main", "String[]" etc. fit together, how they relate to error messages they get when their programs don't compile or don't work.
Then, when they do reach the place in the course when they're to learn all about these concepts, they have to reconcile their own local theories with the material at hand to create a more thorough and evolved understanding of the concept. That's often not an easy process. In many introductory programming courses, there's not much time for reflection. As a result, their evolving understanding is likely closer to their initial understanding instead of the ones we aim at as teachers.
What's not helping here is that as experienced programmers who have developed a deep understanding of these concepts to understand novice's point-of-view and their struggles with the material.
Learning and teaching is hard!
As for the added complexity to the compile or run-time system, that seems mostly an issue for language implementers, not users. It certainly won't be an issue for beginning programmers, so for them and their teachers, there's no tradeoff to pay for.
> Far from using a separate dialect of Java, students can write streamlined declarations for single-class programs and then seamlessly expand their programs to use more advanced features as their skills grow.
I remember when I started with Java 20 years we had a special IDE (BlueJ maybe? DrJava?) that wrapped the main class so that <code>System.out.println("Hello world");</code> was a complete program.
So that is clearly
> Do not introduce a separate beginner's dialect of Java.
Perhaps this is in response to Python growing as an initial programming language (IMHO wrongly because types are fundamental to thinking about programming).
They also mention:
> Reduce the ceremony of writing simple programs such as scripts and command-line utilities.
You can question the merits or trade-offs, but it's very clearly spelled out.
> Reduce the ceremony of writing simple programs such as scripts and command-line utilities.
I think it is important to be able to determine the root cause of a problem first. Do students find difficult to write "public static void main(String[] args)" or do they find difficulty understanding the concepts of a public method, a static method, a void method? Perhaps then we should focus on teaching these concepts, which are essential to java, first, instead of undermining the core of the language and the platform.
Neither "goals" nor "summary" can explain away why this can solve the problem this JEP purports to solve.
This is my opinion, of course.
As the JEP explains, this change does make the concepts of classes and access modifiers easier to teach by allowing the teacher to postpone their introduction to a point in time when they're actually useful and can be understood. Access control, static, and class declarations are only meaningful once you have other classes and/or objects interacting with your code. We don't want to force teachers to teach programming in the large early because many of them told us they don't do that anyway.
> instead of undermining the core of the language and the platform
The syntax and concepts of anonymous classes are unchanged. Providing an unnamed class, implicitly, when a class isn't needed is consistent with how we already provide an unnamed package and an unnamed module implicitly when they're not needed.
In that situation, instructors might spend weeks trying to get their students to understand variables or control flow concepts. Visibility may be a core concern for you, as a (presumably) professional Java programmer, but it's not at all to programming novices.
1. code written by a student gets inserted into a top-level class's main() method (has to be smart enough to deal with imports, inner classes etc.)
2. the top level class might declar certain utility functions such as print() instead of System.out.println(), and possibly others.
Bingo. No need to mess with something that works just fine for 99.99% of the professional users.
edit, in response to @crummy (as we reached the maximum depth it seems):
Some people commented that it might be beneficial to delay introduction of more complex concepts until the basic ones are digested. So hiding the class/main method would help with that (similar to jshell, I might add). And keep in mid this is an IDE and not java-lite.
> Evolving a batch of working declarations in JShell into a real Java program leads to a non-idiomatic style of code because it declares each method, class, and variable as static... it is not the on-ramp programming model we are looking for.
> What I really dislike about this JEP is not only that it tries to solve the educational problem in a worst possible manner (my opinion), but it also risks creating a generation of students who don't really understand the fundamental concepts that allow us to write reliable code.
How would your special IDE/forked Java-lite prevent creating a generation of students who don't really understand the fundamental concepts that allow us to write reliable code?
As Brian Goetz would say, this is a classic “why not just” take.
Both suck. In the first case, students are torn away from the mode of thinking that programming thrives in: that everything is made up of simple, understandable pieces, that can be built up into complex solutions. It ends up serving as a trap for the very kind of inquisitive student that is equipped to succeed in programming. In the second case, everyone's bored to tears, and you can be assured that your entire class will be hopelessly lost before they write their first line of code. It's a bad time all around.
Basically, programming wants to be built up with the following concepts, in order:
1. Algorithmic Thinking (solutions can be representing as a series of simple steps)
2. Structured Syntax (these steps can be represented as structured text)
3. Symbolic Reasoning (types, variables, arrays)
4. Control Flow (loops, conditionals)
5. Functions
6. Higher Structures (classes, objects, etc)
Python, IMO, is pretty good pedagogical tool in this regard
print("Hello World")
"We want our computer to display to us (or 'print' in software terms) the phrase 'Hello World'. Python provides us with a built-in way to do so: the "print" function. In the majority of programming languages, we express a call to a function as a command, followed by its inputs (or 'arguments') contained within parentheses. As our one and only argument, we provide what we want printed, in this case the text "Hello World". By wrapping this text in quotes, we can tell the language that we intend this text to be interpreted as a chunk of literal text, called a 'String' rather than as a variable or other symbol for the language."In contrast, explaining everything going on in the equivalent Java
public class MyFirstClass {
public static void main(String[] args){
System.out.println("Hello World");
}
}
would take more text than I'm willing to hammer into a HN comment, but to be explained to a complete beginner would likely involve skipping, but promising to explain later: classes, functions, "public", "static", "void", command line arguments, and the uniquely weird "System.out", followed by a similar explanation to the Python example, with the added "we end each step with a semicolon".Please see my response to @pgwhalen. In short, the problem has, I think, a much simpler solution.
What I really dislike about this JEP is not only that it tries to solve the educational problem in a worst possible manner (my opinion), but it also risks creating a generation of students who don't really understand the fundamental concepts that allow us to write reliable code.
Perhaps we need to start teaching basic programming with BASIC instead, and graduate to a more complicated platform once the basic concepts have been understood.
But a lot of "universities" treat their "CS" programs as glorified trade schools, and want to focus exclusively on industry-applicable languages, and land on Java because Oracle had a great outreach program. Like it or not, many students will be starting their programming journey in Java, and not fucking it up is probably a good idea.
My approach would have been pretty similar to their listed alternative under "Interpret code units as static members", but that probably comes down to differences over the importance of Object-Oriented principals in beginner programming pedagogy.
Barring that, this proposal really isn't bad. It doesn't break existing code, it does its job well enough. I would have liked there to be support for inline subclasses that can be called from within the same anonymous class environment, but I can see how that would create ambiguity with existing code.
The often touted alternative starting language is C and python, and I believe Java is better than both — C’s mentioned advantage is “learning low level details”, but I don’t see it as necessary, plus C can fail at runtime (or worse, at *some runtime on someone else’s machine) in completely cryptic ways, you may not even realize your program is incorrect until you submit an assignment. I have seen even very smart students be puzzled by it. Also, pointers can be learned afterwards easily, I don’t see it problematic at all.
Python’s problem comes mostly from its dynamically typed nature, it is very hard for new programmers to reason about the runtime state of programs, and why might have it failed on the nth cycle of this loop.
Java is a great in-between. It is not perfect (for example arrays having special syntax is not ideal), but not bad and it being an industry language is just a plus.
Not sure what you mean by "inline subclasses", but an anonymous main class can contain class declarations -- just as classes, named or anonymous can today. This file would be a complete legal program:
record Foo(int x, int y) {}
class Bar { static void print(Foo f) { System.out.println(f); } }
void main() {
Bar.print(makeFoo());
}
Foo makeFoo() { return new Foo(3, 4); } new Object() {
record Foo(int x, int y) {}
class Bar { static void print(Foo f) { System.out.println(f); } }
void main() {
Bar.print(makeFoo());
}
Foo makeFoo() { return new Foo(3, 4); }
}.main();
This JEP merely brings together two existing ideas in Java:1. Anonymous classes
2. Unnamed instances of programming-in-the-large concepts are implicitly provided for you (today this is the case only for packages and modules).
There are no new changes or restrictions to Java syntax in the body of the anonymous class.
I'd say beginners are much more likely to trip over other problems with the language like using == with String instances, the differences between primitives and objects (e.g. copy-by-value vs copy-by-reference, no methods on a primitive, etc.), or the unending problems with null.
Of course, those may never be resolved, and so we'll have to change something that doesn't really matter instead.