1

Why can't I do this in Java:

interface Context{
    void add(Integer o);
}
class Subclass implements Context{
    @Override
    public void add(Object o){...}
}

Wouldn't Subclass.add already implement Context.add since it add(Object) can perform everything add(Integer) can?

What is a good way around this?

Right now I am doing a kind of ugly way:

private void actuallyAdd(Object o){...}
public void add(Object o){actuallyAdd(o);}
public void add(Integer o){actuallyAdd(o);}

EDIT: this is not a duplicate of above question. In the given question, the superclass is the one that has a more general 'Object' as the parameter, while the subclass is more specific. This doesn't work as a more specific method may not be able to handle any Object. In my question the subclass is less specific than the superclass, meaning the subclass is always able to handle whatever the superclass requires.

  • Why can't your interface have `add(Object o)` and override the same in the concrete class? – Pankaj Gadge Jun 05 '18 at 23:53
  • Possible duplicate of [Java - Overriding Object Type Parameter With Concrete Type](https://stackoverflow.com/questions/9632205/java-overriding-object-type-parameter-with-concrete-type) – Logan Jun 05 '18 at 23:55
  • You don't need three methods like you have right now. You'd just need `void add(Object o) { ... }` and `void add(Integer i) { add((Object) i); }` – Jeffrey Jun 06 '18 at 00:54
  • @Jeffrey that is what I had before, but I thought it was even worse, because I would then cast it back in `void add(Object o)`. I am just going to change my class structure to one that is better –  Jun 06 '18 at 01:10

2 Answers2

4

This occurs because the Java compiler requires signatures of overriding methods to be a "subsignature" of the overridden method. That is, either the types of the parameters are identical, or the erasures of the types of the parameters are identical.

The JLS, Section 8.4.8.1, states this as follows:

An instance method mC declared in or inherited by class C, overrides from C another method mI declared in interface I, iff all of the following are true:

...

  • The signature of mC is a subsignature (§8.4.2) of the signature of mI.

Section 8.4.2:

The signature of a method m1 is a subsignature of the signature of a method m2 if either:

  • m2 has the same signature as m1, or

  • the signature of m1 is the same as the erasure (§4.6) of the signature of m2.

You can work around this by declaring a generic type parameter, as the other answer indicates, although it's unclear if the original purpose of the interface is to take an Integer or just some specific yet unknown type.

Community
  • 1
  • 1
rgettman
  • 176,041
  • 30
  • 275
  • 357
  • Oh, I see, though not sure why that limitation is in place. The answer you mentioned does not work for me though (see my comment on that answer) –  Jun 06 '18 at 00:20
  • That is the only workaround that I see. Unlike covariant return types for overriding methods (i.e. narrower return types for methods that override), introduced in Java 5, the parameter types are invariant due to the requirements listed above. What you are aiming for is contravariance in parameter types, which is [not supported in Java](https://stackoverflow.com/questions/12439649/why-are-contravariant-parameter-types-in-java-not-allowed-for-overriding). – rgettman Jun 06 '18 at 00:25
  • Thanks! I guess I will have to change my model then –  Jun 06 '18 at 00:27
2

You could try generics:

interface Context<T> {
    void add(T t);
}

class Subclass implements Context<Object> {
    @Override
    public void add(Object o){...}
}
Bohemian
  • 412,405
  • 93
  • 575
  • 722
  • I can't think of how this would work, since I in my application I need to do something like `void consume(Context thing)`. And you cannot call consume with a Context –  Jun 06 '18 at 00:16
  • @AJC [Generic wildcards](https://docs.oracle.com/javase/tutorial/extra/generics/wildcards.html) might be able to help you here. If your method's signature was `void consume(Context super Integer> thing)`, then you could call it with a `Context` or a `Context` (or a `Context`, etc.). – Jeffrey Jun 06 '18 at 00:52