2

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.

aviad
  • 8,229
  • 9
  • 50
  • 98
  • What do you mean by "breaks"? JDBC has added methods to their interfaces between versions without breaking older versions. – Peter Lawrey Sep 24 '12 at 08:41
  • @Peter Lawrey, I mean that if ClassA implements IfsA and you ship the IfsA to your customer then when you add a new method to IfsA and the client that has the old verion of IfsA will try to access the methods he had in the old IfsA he will get runtime error. Check the Erich Gamma answer here (search for " In Java when you add a new method to an interface") http://www.artima.com/lejava/articles/designprinciples.html – aviad Sep 24 '12 at 08:46
  • @Peter Lawrey, actually the answer to what you are asking is inside your question :) "...between versions..." - this is not the situation that described in my question. – aviad Sep 24 '12 at 08:50
  • You make breaking API changes but you don't consider this a version change? – Peter Lawrey Sep 24 '12 at 08:52
  • @Peter Lawrey, that's not what I was saying. – aviad Sep 24 '12 at 08:55
  • That clarifies things then. ;) So what are you saying? – Peter Lawrey Sep 24 '12 at 08:57
  • I do call this a version change. My question was why this 'version change' cannot be treated in more flexible way? The clients having the old version of the interface would continue to work with it. – aviad Sep 24 '12 at 09:08
  • There a number of situation where adding or removing a method doesn't cause a problem. See my answer. I am trying to work out what changes you see as causing a problem and what the use case of that change would be. – Peter Lawrey Sep 24 '12 at 09:18

4 Answers4

3

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.

James Martinez
  • 191
  • 2
  • 11
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • Correct. When you only recompile the interface, your compiled Main class will internally still use the old interface version. This is not java specific, but also happens in e.g. vb .NET (and I assume other other languages). When you do not have a good build order (referenced classes before referencing classes) you will get into trouble at runtime, when you referencing classes still rely on older versions. Trust me, it takes you ages to find that problem. ;) – LuigiEdlCarno Sep 24 '12 at 09:21
  • Tried it also. Works like a charm. Well, I guess this makes the article I mentioned(http://www.artima.com/lejava/articles/designprinciples.html) and the specific quote by Erich Gamma misleading! We should read it as: "In Java when you add a new method to an interface, you DO NOT break all your clients" (no worries:) – aviad Sep 24 '12 at 09:24
  • When you recompile the interface there is only the new version at runtime. If you make an incompatible change it will throw an error, but adding a method is not an incompatible change. If I rename `method1` or change ti signature, even its return type which is ignored, then there is a problem. – Peter Lawrey Sep 24 '12 at 09:25
  • I am not saying you can't break clients. e.g. if you overload a method which wasn't overloaded before, your calling code will not use the new method. You have to beware of this and not rely on clients recompiling. If you change the return type, even if its ignored, it will break the sub-class. – Peter Lawrey Sep 24 '12 at 09:27
  • upvote & accept. However, I am afraid now then that I has missed the point of the above statement by Erich G. ... feel a little confused :) – aviad Sep 24 '12 at 09:27
  • Perhaps looking at all the possible ways you could have problems is more dangerous than just saying interfaces for a version have to be immutable. When a new version comes alone you use a different package or class name. This is the safest option. However, you could fall into the trap of assuming that if the client has the wrong version this will be obvious in some way because it could be working for years with a version you don't expect they are using. – Peter Lawrey Sep 24 '12 at 09:40
1

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

LuigiEdlCarno
  • 2,410
  • 2
  • 21
  • 37
0

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

Shailendra
  • 8,874
  • 2
  • 28
  • 37
0

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.