5

Why can't I access static methods of an interface using an instance variable.

public class TestClass {
    public static void main(String[] args) {
        AWD a = new Car();
        a.isRearWheelDrive(); //doesn't compile
    }
}

interface AWD {
    static boolean isRearWheelDrive() {
        return false;
    }  
}

class Car implements AWD {
}
Joshua Taylor
  • 84,998
  • 9
  • 154
  • 353
chamina
  • 498
  • 3
  • 15
  • 1
    The only related quote I could find in the Java 8 [specifications](https://docs.oracle.com/javase/specs/jls/se8/html/jls-9.html) goes as follows: "An interface can declare static methods, which are invoked without reference to a particular object." – Mena May 25 '17 at 10:29
  • 1
    Possible duplicate of [In java 8, why cannot call the interface static method that the current class is implementing](https://stackoverflow.com/questions/29383083/in-java-8-why-cannot-call-the-interface-static-method-that-the-current-class-is) – Joshua Taylor May 25 '17 at 10:59
  • 1
    Why would you want to? – Lew Bloch May 25 '17 at 13:43

2 Answers2

10

Static Interface Methods Aren't Inherited by Subclasses

You can't access static methods of interfaces through instances. You have to access them statically. This is a bit different from classes where accessing a static method through an instance is allowed, but often flagged as a code smell; static methods should be accessed statically.

That's because static methods of classes are inherited by subclasses, but static methods of interfaces aren't. That's stated in §8.4.8 of the specification:

8.4.8. Inheritance, Overriding, and Hiding

A class does not inherit static methods from its superinterfaces.

When you are looking up the accessible methods for the instance, the static method from the interface isn't among them.

Options for the code

So, as the code is now, you need to access the method statically:

AWD.isRearWheelDrive()

However, it seems like you want this to be an instance method, in which case you should probably be using a default method that returns false:

interface AWD {
  default boolean isRearWheelDrive() {
    return false;
  }
}

Even that seems a little bit odd, though. It seems like you'd probably want that default method to be overriding some non-default method in a super-interface. That is, you probably want something like:

  interface HasDriveWheels {
    boolean isRearWheelDrive();
  }

  interface AllWheelDrive extends HasDriveWheels {
    @Override
    default boolean isRearWheelDrive() {
      return false;
    }
  }
Joshua Taylor
  • 84,998
  • 9
  • 154
  • 353
  • @JoshyaTaylor I suspect OP knows that and is asking why (which seems like a good question since the same limitation does not apply to classes). Have you found any specific statement in the Java specs illustrating this? – Mena May 25 '17 at 10:31
  • @Mena the reasoning is the same as for the pure existance of interfaces: If you allow access to static method in this way, you would have the diamond problem all over again. – Turing85 May 25 '17 at 10:41
  • @Turing85 I agree, but I'm just curious as per why I can't find any explicit statement in the Java specs :) – Mena May 25 '17 at 10:43
  • 1
    I'm still looking at the spec, but I suspect that this has to do with how the method to invoke is found. In the past, when interfaces didn't have static methods, there would have been no language for "search the static methods of the *interfaces* of the type of the object"; adding that in would mean introducing potential future ambiguity if a static method was added to an interface. – Joshua Taylor May 25 '17 at 10:43
  • 1
    @JoshuaTaylor [here you go](https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.12). – Turing85 May 25 '17 at 10:45
  • @Turing85 Yup, I'm already wading through parts of that. Haven't found the specific bit yet. I think that it's going to fall into which methods are accessible. – Joshua Taylor May 25 '17 at 10:47
  • @Mena Turing85 linked to JLS7, not JLS8. – Joshua Taylor May 25 '17 at 10:48
  • 1
    I think it's [8.4.8](http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.4.8), "a class does not inherit `static` methods from its superinterfaces." I found that in another question that's probably not quite a dupe (it already assumes knowing the answer): https://stackoverflow.com/questions/29383083/in-java-8-why-cannot-call-the-interface-static-method-that-the-current-class-is. – Joshua Taylor May 25 '17 at 10:54
  • @JoshuaTaylor kudos. – Mena May 25 '17 at 10:55
3

This is specified in The Java® Language Specification, §15.12.3. Compile-Time Step 3: Is the Chosen Method Appropriate?

If the form is ExpressionName . [TypeArguments] Identifier or Primary . [TypeArguments] Identifier, then the compile-time declaration must not be a static method declared in an interface, or a compile-time error occurs.

In retrospect, the ability to invoke a static method through an instance is of little use, even less than the inheritance of static methods. I’m quite sure that a lot of developers consider it a design mistake that is only kept due to compatibility reasons.

For the newer feature of static methods in an interface, there was no compatibility constraint that required repeating this mistake, hence, the rules for static methods in interfaces were designed differently. This is also the solution with the smallest impact on compatibility with old code.

Naman
  • 27,789
  • 26
  • 218
  • 353
Holger
  • 285,553
  • 42
  • 434
  • 765