TL;DR
In Java, the reason of public static void main(String[] args)
is that
- Gosling wanted
- the code written by someone experienced in C (not in Java)
- to be executed by someone used to running PostScript on NeWS
For C#, the reasoning is transitively similar so to speak. Language designers kept the program entry point syntax familiar for programmers coming from Java. As C# architect Anders Hejlsberg puts it,
...our approach with C# has simply been to offer an alternative... to Java programmers...
Long version
expanding above and backed up with boring references.
java Terminator Hasta la vista Baby!
VM Spec, 2.17.1 Virtual Machine Start-up
...The manner in which the initial class is specified to the Java virtual machine is beyond the scope of this specification, but it is typical, in host environments that use command lines, for the fully qualified name of the class to be specified as a command-line argument and for subsequent command-line arguments to be used as strings to be provided as the argument to the method main. For example, using Sun's Java 2 SDK for Solaris, the command line
java Terminator Hasta la vista Baby!
will start a Java virtual machine by invoking the method main of class Terminator
(a class in an unnamed package) and passing it an array containing the four strings "Hasta", "la", "vista", and "Baby!"...
...see also: Appendix: I need your clothes, your boots and your motorcycle
- My interpretation:
execution targeted for use like typical scripts in command line interface.
important sidestep
...that helps avoid a couple of false traces in our investigation.
VM Spec, 1.2 The Java Virtual Machine
The Java virtual machine knows nothing of the Java programming language...
I noticed above when studying prior chapter - 1.1 History which I thought could be helpful (but turned out useless).
- My interpretation:
execution is governed by VM spec alone, which
explicitly declares that it has nothing to do with Java language
=> OK to ignore JLS and anything Java language related at all
Gosling: a compromise between C and scripting language...
Based on above, I began searching the web for JVM history. Didn't help, too much garbage in results.
Then, I recalled legends about Gosling and narrowed down my search to Gosling JVM history.
Eureka! How The JVM Spec Came To Be
In this keynote from the JVM Languages Summit 2008, James Gosling discusses... Java's creation,... a compromise between C and scripting language...
- My interpretation:
explicit declaration that at the moment of creation,
C and scripting have been considered most important influences.
Already seen nod to scripting in VM Spec 2.17.1,
command line arguments sufficiently explain String[] args
but static
and main
aren't there yet, need to dig further...
Note while typing this - connecting C, scripting and VM Spec 1.2 with its nothing-of-Java - I feel like something familiar, something... object oriented is slowly passing away. Take my hand and keep movin' Don't slow down we're nearly there now
Keynote slides are available online: 20_Gosling_keynote.pdf, quite convenient for copying key points.
page 3
The Prehistory of Java
* What shaped my thinking
page 9
NeWS
* Networked Extensible Window System
* A window system based on scripting....
PostScript (!!)
page 16
A Big (but quiet) Goal:
How close could I get to a
"scripting" feel...
page 19
The original concept
* Was all about building
networks of things,
orchestrated by a scripting
language
* (Unix shells, AppleScript, ...)
page 20
A Wolf in Sheeps Clothing
* C syntax to make developers
comfortable
A-ha! Let's look closer at C syntax.
The "hello, world" example...
main()
{
printf("hello, world\n");
}
...a function named main is being defined. The main function serves a special purpose in C programs; the run-time environment calls the main function to begin program execution.
...The main function actually has two arguments, int argc
and char *argv[]
, respectively, which can be used to handle command line arguments...
Are we getting closer? you bet. It is also worth following "main" link from above quote:
the main function is where a program starts execution. It is responsible for the high-level organization of the program's functionality, and typically has access to the command arguments given to the program when it was executed.
- My interpretation:
To be comfortable for C developer, program entry point has to be main
.
Also, since Java requires any method to be in class, Class.main
is
as close as it gets: static invocation, just class name and dot,
no constructors please - C knows nothing like that.
This also transitively applies to C#, taking into account
the idea of easy migration to it from Java.
Readers thinking that familiar program entry point doesn't matter are kindly invited to search and check Stack Overflow questions where guys coming from Java SE are trying to write Hello World for Java ME MIDP. Note MIDP entry point has no main
nor static
.
Conclusion
Based on above I would say that static
, main
and String[] args
were at the moments of Java and C# creation most reasonable choices to define program entry point.
Appendix: I need your clothes, your boots and your motorcycle
Have to admit, reading VM Spec 2.17.1 was enormous fun.
...the command line
java Terminator Hasta la vista Baby!
will start a Java virtual machine by invoking the method main of class Terminator
(a class in an unnamed package) and passing it an array containing the four strings "Hasta", "la", "vista", and "Baby!".
We now outline the steps the virtual machine may take to execute Terminator
, as an example of the loading, linking, and initialization processes that are described further in later sections.
The initial attempt... discovers that the class Terminator
is not loaded...
After Terminator
is loaded, it must be initialized before main can be invoked, and a type (class or interface) must always be linked before it is initialized. Linking (§2.17.3) involves verification, preparation, and (optionally) resolution...
Verification (§2.17.3) checks that the loaded representation of Terminator
is well formed...
Resolution (§2.17.3) is the process of checking symbolic references from class Terminator
...
Symbolic references from Terminator
oh yeah.
If you look at the Java Language Specification (http://docs.oracle.com/javase/specs/) you will find a distinction between the static type and the dynamic type of object references.
The static type of any reference refers to the type declared in your program at development time.
The dynamic type of the reference refers to the type of object which is actually stored at the reference address at runtime.
So if you have a simple hierarchy, where A is the superclass and B is a subclass of A.
class A {
public String a = "super";
void m() {...}
}
class B extends A {
public String a = "sub";
@Override
void m() {...}
}
A refA = new A();
A refB = new B();
B refB2 = new B();
refA.m();
refB.m();
System.out.println(refA.a);
System.out.println(refB.a);
System.out.println(refB2.a);
For example, refB's static type is A but its dynamic type is B. What does this imply at runtime?
- Instance variables are statically bound, i.e. you can only access the instance variables of the static type, so the first two println() statements will print "super", since A is the static type of refA and refB. The last println() statement will in contrast print "sub", because in this case B is the static type.
- Methods are dynamically bound, i.e. the invoked method is first searched in the dynamic type of the reference and if it isn't found, the class hierarchy is traversed up until a definition of the overriden method is found (this mechanism is called dynamic dispatch). This means that in case refA.m() method void m() of A is invoked and in case refB.m(), void m() of B is invoked.
Best Answer
The concept you initially refer to in your question is called covariant return types.
Covariant return types work because a method is supposed to return an object of certain type and overriding methods may actually return a subclass of it. Based on the subtyping rules of a language like Java, if
S
is a subtype ofT
, then whereverT
appears we can pass anS
.As such it is safe to return an
S
when overriding a method that expected aT
.Your suggestion to accept that an overriding a method uses arguments that are subtypes of those requested by the overridden method is much more complicated since it leads to unsoundness in the type system.
By one hand, by the same subtyping rules mentioned above, most likely it already works for what you want to do. For instance
Nothing prevents implementations of this class from receiving any kind of animal, as such it already satisfies the criteria in your question.
But let's suppose we could override this method as you suggested:
Here's the funny part, now you could do this:
As per the public interface of
AnimalHunter
you should be able to hunt any animal, but as per your implementation ofMammutHunter
you only acceptMammut
objects. Therefore the overriden method does not satisfy the public interface. We just broke the soundness of the type system here.You can implement what you want by using generics.
Then you could define your MammutHunter
And using generic covariance and contravariance you can relax the rules in your favor when necessary. For instance we could make sure that a mammal hunter can only hunt felines in a given context:
Supposing
MammalHunter
implementsAnimalHunter<Mammal>
.In that case this would not be accepted:
Even when mammuts are mammals it would not be accepted due to the restrictions on the contravariant type we are using here. So, you can still excercice some controll over the types to do things like the ones you mentioned.