tags:

views:

247

answers:

4

In Java, you can do the following :

public interface IEngine{}
public interface ICoolEngine extends IEngine{}

public interface Car
{
   IEngine getEngine();
}
public interface ICoolCar extends ICar
{
    @Override
    ICoolEngine getEngine();
}

While this nicely solves a problem I've been grappling with, something about it "feels" wrong.

Am I committing some nasty design faux pas here?

Regards

Marty

+1  A: 

No, that's fine. Since ICoolEngine extends IEngine, any object implementing ICoolEngine can be treated as if it's an IEngine (without all the ICoolEngine-specific methods of course). You'll just have to be aware of the type difference depending on which interface you are working with in each situation, and make sure not to use ICoolEngine methods that aren't defined in IEngine (assuming that, in your actual code, there are additional methods listed in the equivalent of ICoolEngine).

It's not a bad practice to do this; you're simply using the power of polymorphism.

Marc W
+12  A: 

No, you are doing the right thing. Covariant returns just specify that the class, and classes below it, must return a specific subclass of the original general class argument that the parent class returned. It also means that your subclasses are still compatible with the original interface that requires that it return an Engine, but if you know that it is an ICoolCar, that it has an ICoolEngine - because the more specific interface knows of more specific functionality. This applies to interfaces as well as classes - this is correct, proper and useful to boot.

MetroidFan2002
+1  A: 

Covariant return types is a deliberate feature that was added in 1.5 (to support generics primarily).

@Override may not work for overriding abstract methods with some compilers (javac was updated in 1.6, but the JLS amendment was missed out).

As always adding an method to an interface risks compatibility issues. Redeclaring a method exactly as in the super-type would be fine, but changing the return type causes a bridge method in implementation classes. This is why Iterable.iterator does not return a read-only version of the Iterator interface.

Tom Hawtin - tackline
A: 

What you're doing is completely OK.

I'd prefer to put it this way:

public interface IEngine { }
public interface ICoolEngine extends IEngine { }

public interface ICar<T extends IEngine> {
   T getEngine();
}

public interface ICoolCar extends ICar<ICoolEngine> { }

I used generics because as you used an annotation I guessed you're working on Java 5+

victor hugo
I'm not going to downvote this answer, because it's interesting. However, I can't say I like it, because it seems to use generics just for the sake of using generics, when it does not have to use generics at all. The fact that the ambiguity is there with your two generic implementations of the ICoolCar interface illustrates this point - the generics add unneeded complication to this. The original definition made it clear that it returned an instance of ICoolEngine - no generics needed, and no ambiguity as well.
MetroidFan2002
Also, covariant types were added in Java 5 so the whole question would have been moot anyway.
James
My solution is better if you need the interface implementation to specify 'getEngine()' return-type.I use something like this when programmed a GenericDao, its implementations needed to specify the return-type and parameter-type of many methods.I'm glad you didn't downvoted cuz I still think is a good alternative
victor hugo