0

I want to override a method in Java by passing a concrete subtype of the parameter type that is specified in the super class. See the following example. Is there a way to get this working in Java?

    interface A {
        <T> T f(V<T> v);
    }

    interface V<T> {
        T v();
    }

    class B implements A {

        @Override // Here it gives error: Method does not override method from its superclass
        public <T> T f(V1<T> v) {
            return v.v1();
        }
    }

    interface V1<T> extends V<T> { 
        T v1();
    }

I have also tried this version for A but doesn't work:

interface A {
   <T, U extends V<T>> T f(U v);
}
Alexander Ivanchenko
  • 25,667
  • 5
  • 22
  • 46
Wickoo
  • 6,745
  • 5
  • 32
  • 45
  • 3
    This looks actually impossible, because you can't parameterize on higher-kinded types in Java. – Louis Wasserman Jun 22 '22 at 17:18
  • 1
    Can't work because if you casted B back to A and called f with a V, it would have the wrong type as f is overridden to accept V1. – akarnokd Jun 22 '22 at 17:20
  • So what's the workaround here? Use `instanceof` and detect the actual type of `V` at run time? – Wickoo Jun 22 '22 at 17:21
  • @LouisWasserman is it possible to do in Scala or Kotlin? – Wickoo Jun 22 '22 at 17:22
  • Not in Kotlin. Scala might be possible, I don't know it. – Louis Wasserman Jun 22 '22 at 17:23
  • 2
    Doesn't work. Same would be having `A.f(Object o)` then trying to override it as `B.f(String s)`. – akarnokd Jun 22 '22 at 17:25
  • @akarnokd right, I guess I need to rely on runtime checks to detect the actual type if I want to access a specific method there. – Wickoo Jun 22 '22 at 17:28
  • Yes. *(10 chars)* – akarnokd Jun 22 '22 at 17:30
  • Does an implementor of `A` support *all* subclasses of `V>`, or is an implementor of `A` required to support only *one* subclass (at their choosing)? If it's the latter, then the generic should be on the interface, because your current code shown makes a very strong guarantee that it sounds like (based on the comments) you didn't intend. – Silvio Mayolo Jun 22 '22 at 18:50

2 Answers2

0

The definition of the method f in the subclass violates the Liskov substitution principle, which says that should be able to use a child instance whenever the parent is expected.

public <T> T f(V1<T> v)

Method f in the parent class expects V<T> as an argument. And V is a super type of V1 which means that the method declared by the child expects a more narrow type. I.e. the instance of a child is not able to handle all subtypes of V and hence can't substitute its parent.

And from the practical point of view, it's clearly not a valid method overriding (at least in Java). Method signatures should match exactly, otherwise it's a method overloading, which is the case here.

And you're getting a compilation error because you've applied @Override annotation and the compiler fails to find a method with matching signature in the parent class, and also because the contract defined by the interface A hasn't been't implemented.

The only exception related to the overriding of generic methods that we have is the possibility to override a method that expects a generic parameter like Collection<T> to be overridden by a non generified method that expects a Collection of row type (it was done in order to facilitate transition to generics when they were introduced in the language).

Alexander Ivanchenko
  • 25,667
  • 5
  • 22
  • 46
0

You can get close to what you want by taking multiple generic arguments at the interface level and none at the function level.

interface A<T, S extends V<T>> {
    public T f(S v);
}

class B<T> extends A<T, V1<T>> {

    @Override // Here it gives error: Method does not override method from its superclass
    public T f(V1<T> v) {
        return v.v1();
    }
}

Note that this isn't quite the guarantee you were looking for, as someone could come along and write some class C extends A<Integer, V1<Integer>> that only works for integers and no other type. But to do that, you'd need something called higher-kinded polymorphism which is not supported in Java.

Silvio Mayolo
  • 62,821
  • 6
  • 74
  • 116