Pregunta

In Java when you add a new method to an interface, you break all your clients. When you have an abstract class, you can add a new method and provide a default implementation in it. All the clients will continue to work.

I wonder why the interface is designed this way?

All the old methods are still there, so seems like there is no backward compatibility issue. (Of course there need to be certain exceptions, but I think enabling to add new methods to java interfaces without breaking the clients could be really good idea...)

I'd appreciate your comments.

¿Fue útil?

Solución

There are a few possible breaks I can see

  • you assume that clients will use a new overloaded method, but they don't because the code hasn't been recompiled.
  • you add a method which the client already had which does something different.
  • you add a method which means their subclasses break when recompiled. IMHO This is desirable.
  • you change the return type, method name or parameters types which will cause an Error at runtime.
  • you swap parameters of the same type. This is possibly the worst and most subtle bug. ;)

IMHO, It's the subtle problems which are more likely to cause you grief. However, I wouldn't assume that simply adding a method will break the code and I wouldn't assume that if a client's code isn't getting runtime error means they are using the latest version of anything. ;)


If I compile this code

public interface MyInterface {
    void method1();

    // void method2();
}

public class Main implements MyInterface {
    @Override
    public void method1() {
        System.out.println("method1 called");
    }

    public static void main(String... args) {
        new Main().method1();
    }
}

it prints

method1 called

I then uncomment method2() and recompile just the interface. This means the interface has a method the Main doesn't implement. Yet when I run it without recompiling Main I get

method1 called

If I have

public class Main implements MyInterface {    
    public void method1() {
        System.out.println("method1 called");
    }

    public void method2() {
        System.out.println("method2 called");
    }

    public static void main(String... args) {
        new Main().method1();
    }
}

and I run with // method2() commented out, I don't have a problem.

Otros consejos

An interface is like a template for a class. When you have an object whose class implements a certain interface and you do a cast to that interface you can only access that object (and it's methods) through the interface. Thus, your client will always see all the methods provided by the interface and not only those that are in fact implemented by the class.

Your suggestion would result in you wondering whether the object you are handling at any moment does really have an implementation for the method they are trying to call.

Of course in your scenarion that would not happen for the legacy clients, until you want to update them some time and you rely on your objects having an implementation for all the methods your IDE previews you. :)

The fact with abstract classes is (exactly as you have mentioned) that you provide a default implementation and can thus, on the client side, rely on your object having the methods implemented.

Hope this helps to clear things up.

Regards

Interface represents a set of ALL methods available from that interface to its user and not SOME of the methods to which other methods could be added. It’s a contract of being no less and no more.

The best thing about this design is that there is no ambiguity. Java being a statically typed language and needs to know completely what all are available methods from the Interface declaration. Inside JVM there is only one representation of Interface class and it needs to contain the complete set of abstract methods available.

When you have an abstract class with some implemented methods, it semantics of being a class which cannot be instantiated but whose sole purpose is to be extended and have its abstract methods implemented. An abstract class enforces IS-A relation whereas Interface enforces BEHAVES-AS

The role of Interfaces are more than just declarations of methods. They form the very basis for Contract based/first development. These contract apis define what goes as input and what comes as output. And like any CONTRACT they are valid only when all conditions are met. And that is the reason it is mandatory for an class to implement all the apis of the implemented interface.

There are design patters that define the way to handle versions and new developments. Factory/Abstract Factory patters which should be used when instantiating the implementations of these Interfaces (So that they can internally validate the proper version is implementation to be used).

The flexibilities that you are asking for can be implemented by proper design, instead of expecting it from programming language. For a programming tool plugging in an new version implementation into an old interface is a programming error. But it is different from backward compatibility breaks. Availability of a newer version of component doesn't necessarily mean there will be a Backward compatibility break. A matured product or component always supports older versions as long as they don't become a real pain for maintenance. So in most cases user don't have to be worried about the availability of a newer version unless there are some new features that you would like make use of.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top