Saturday 28 October 2017

Why can't I define a static method in a Java interface?

itemprop="text">

EDIT:
As of Java 8, static methods are now allowed in
interfaces.



Here's the
example:



public interface
IXMLizable
{

static T
newInstanceFromXML(Element e);
Element
toXMLElement();
}


Of
course this won't work. But why not?



One of the
possible issues would be, what happens when you
call:



IXMLizable.newInstanceFromXML(e);



In
this case, I think it should just call an empty method (i.e. {}). All subclasses would
be forced to implement the static method, so they'd all be fine when calling the static
method. So why isn't this
possible?



EDIT: I
guess I'm looking for answer that's deeper than "because that's the way Java is".



Is there a particular technological reason why
static methods can't be overwritten? That is, why did the designers of Java decide to
make instance methods overrideable but not static
methods?



EDIT: The
problem with my design is I'm trying to use interfaces to enforce a coding
convention.




That is, the goal of the
interface is
twofold:




  1. I want
    the IXMLizable interface to allow me to convert classes that implement it to XML
    elements (using polymorphism, works
    fine).


  2. If someone wants to make a new
    instance of a class that implements the IXMLizable interface, they will always know that
    there will be a newInstanceFromXML(Element e) static
    constructor.




Is
there any other way to ensure this, other than just putting a comment in the
interface?


itemprop="text">
class="normal">Answer







With Java 8,
interfaces can have static methods. They can also have concrete
instance methods, but not instance fields.



There
are really two questions here:




  1. Why, in the bad old days,
    couldn't interfaces contain static methods?

  2. Why can't
    static methods be
    overridden?






There
was no strong technical reason why interfaces couldn't have had static methods in
previous versions. This is href="https://stackoverflow.com/questions/129267/why-no-static-methods-in-interfaces-but-static-fields-and-inner-classes-ok/135722#135722">summed
up nicely by the poster of a duplicate question. Static interface methods were
initially considered as rel="noreferrer">a small language change, and then there was href="http://docs.google.com/Doc?docid=dfkwr6vq_30dtg2z9d8&hl=en"
rel="noreferrer">an official proposal to add them in Java 7, but it was
later rel="noreferrer">dropped due to unforeseen
complications.



Finally, Java 8
introduced static interface methods, as well as override-able instance methods with a
default implementation. They still can't have instance fields though. These features are
part of the lambda expression support, and you can read more about them in href="http://jcp.org/en/jsr/detail?id=335" rel="noreferrer">Part H of JSR
335.





The answer
to the second question is a little more
complicated.




Static methods are
resolvable at compile time. Dynamic dispatch makes sense for instance methods, where the
compiler can't determine the concrete type of the object, and, thus, can't resolve the
method to invoke. But invoking a static method requires a class, and since that class is
known statically—at compile time—dynamic dispatch is
unnecessary.



A little background on how instance
methods work is necessary to understand what's going on here. I'm sure the actual
implementation is quite different, but let me explain my notion of method dispatch,
which models observed behavior
accurately.



Pretend that each class has a hash
table that maps method signatures (name and parameter types) to an actual chunk of code
to implement the method. When the virtual machine attempts to invoke a method on an
instance, it queries the object for its class and looks up the requested signature in
the class's table. If a method body is found, it is invoked. Otherwise, the parent class
of the class is obtained, and the lookup is repeated there. This proceeds until the
method is found, or there are no more parent classes—which results in a
NoSuchMethodError.



If a
superclass and a subclass both have an entry in their tables for the same method
signature, the sub class's version is encountered first, and the superclass's version is
never used—this is an "override".



Now, suppose
we skip the object instance and just start with a subclass. The resolution could proceed
as above, giving you a sort of "overridable" static method. The resolution can all
happen at compile-time, however, since the compiler is starting from a known class,
rather than waiting until runtime to query an object of an unspecified type for its
class. There is no point in "overriding" a static method since one can always specify
the class that contains the desired
version.




/>



Here's a little more material
to address the recent edit to the question.



It
sounds like you want to effectively mandate a constructor-like method for each
implementation of IXMLizable. Forget about trying to enforce
this with an interface for a minute, and pretend that you have some classes that meet
this requirement. How would you use
it?



class Foo implements
IXMLizable {
public static Foo newInstanceFromXML(Element e) { ...
}

}

Foo obj =
Foo.newInstanceFromXML(e);


Since
you have to explicitly name the concrete type Foo when
"constructing" the new object, the compiler can verify that it does indeed have the
necessary factory method. And if it doesn't, so what? If I can implement an
IXMLizable that lacks the "constructor", and I create an
instance and pass it to your code, it is an
IXMLizable with all the necessary
interface.



Construction is part of the
implementation,
not the interface. Any code that works successfully with the
interface doesn't care about the constructor. Any code that cares about the constructor
needs to know the concrete type anyway, and the interface can be
ignored.


No comments:

Post a Comment

php - file_get_contents shows unexpected output while reading a file

I want to output an inline jpg image as a base64 encoded string, however when I do this : $contents = file_get_contents($filename); print &q...