Search code examples
javainterfacestatic-typingduck-typingstructural-typing

Why interfaces must be declared in Java?


Sometimes we have several classes that have some methods with the same signature, but that don't correspond to a declared Java interface. For example, both JTextField and JButton (among several others in javax.swing.*) have a method

public void addActionListener(ActionListener l)

Now, suppose I wish to do something with objects that have that method; then, I'd like to have an interface (or perhaps to define it myself), e.g.

  public interface CanAddActionListener {
      public void addActionListener(ActionListener l);
  }

so that I could write:

  public void myMethod(CanAddActionListener aaa, ActionListener li) {
         aaa.addActionListener(li);
         ....

But, sadly, I can't:

     JButton button;
     ActionListener li;
     ...
     this.myMethod((CanAddActionListener)button,li);

This cast would be illegal. The compiler knows that JButton is not a CanAddActionListener, because the class has not declared to implement that interface ... however it "actually" implements it.

This is sometimes an inconvenience - and Java itself has modified several core classes to implement a new interface made of old methods (String implements CharSequence, for example).

My question is: why this is so? I understand the utility of declaring that a class implements an interface. But anyway, looking at my example, why can't the compiler deduce that the class JButton "satisfies" the interface declaration (looking inside it) and accept the cast? Is it an issue of compiler efficiency or there are more fundamental problems?

My summary of the answers: This is a case in which Java could have made allowance for some "structural typing" (sort of a duck typing - but checked at compile time). It didn't. Apart from some (unclear for me) performance and implementations difficulties, there is a much more fundamental concept here: In Java, the declaration of an interface (and in general, of everything) is not meant to be merely structural (to have methods with these signatures) but semantical: the methods are supposed to implement some specific behavior/intent. So, a class which structurally satisfies some interface (i.e., it has the methods with the required signatures) does not necessarily satisfies it semantically (an extreme example: recall the "marker interfaces", which do not even have methods!). Hence, Java can assert that a class implements an interface because (and only because) this has been explicitly declared. Other languages (Go, Scala) have other philosophies.


Solution

  • Why can't the compiler deduce that the class JButton "satisfies" the interface declaration (looking inside it) and accept the cast? Is it an issue of compiler efficiency or there are more fundamental problems?

    It is a more fundamental issue.

    The point of an interface is to specify that there is a common API / set of behaviors that a number of classes support. So, when a class is declared as implements SomeInterface, any methods in the class whose signatures match method signatures in the interface are assumed to be methods that provide that behavior.

    By contrast, if the language simply matched methods based on signatures ... irrespective of the interfaces ... then we'd be liable to get false matches, when two methods with the same signature actually mean / do something semantically unrelated.

    (The name for the latter approach is "duck typing" ... and Java doesn't support it.)


    The Wikipedia page on type systems says that duck typing is neither "nominative typing" or "structural typing". By contrast, Pierce doesn't even mention "duck typing", but he defines nominative (or "nominal" as he calls it) typing and structural typing as follows:

    "Type systems like Java's, in which names [of types] are significant and subtyping is explicitly declared, are called nominal. Type systems like most of the ones in this book in which names are inessential and subtyping is defined directly on the structure of the types, are called structural."

    So by Pierce's definition, duck typing is a form of structural typing, albeit one that is typically implemented using runtime checks. (Pierce's definitions are independent of compile-time versus runtime-checking.)

    Reference:

    • "Types and Programming Languages" - Benjamin C Pierce, MIT Press, 2002, ISBN 0-26216209-1.