Why is there a general argument that “extends” something unacceptable in a derived function, but a common return type?

I would like to override a generic function in a subclass as follows:

superclass:

public abstract class Metric {

    public abstract <T extends Foo> T precompute(); // valid syntax
    public abstract <T extends Foo> void distance(T arg); // valid syntax
    public static class Foo {}

}

Subclass:

public class MetricDefault extends Metric {

    @Override
    public Bar precompute() { return new Bar(); } // Valid - return type extends Foo

    @Override
    public void distance(Bar arg) {} // Invalid ??? - argument type extends foo

    public static class Bar extends Metric.Foo {}

}

A generic function, when the generic type is the return value of a function, is valid Java code and succeeds.

Changing only the placement of the generic type - turning it into an argument to the function, not the return type - and this becomes invalid Java code.

Why is the last case invalid? What can I do to implement this feature?

+3
source share
5 answers
public abstract class Metric<T extends Foo> {

    public abstract  T precompute(); // valid syntax
    public abstract void distance(T arg); // valid syntax
    public class Foo {}

}

then if I realized that Bar extends Foo:

public class MetricDefault extends Metric<Bar> {

    @Override
    public Bar precompute() { return new Bar(); } 

    @Override
    public void distance(Bar arg){} 

    public class Bar extends Foo {}

}
+3
source

Method Declaration

public abstract <T extends Foo> void distance(T arg);

, T, , Foo T.

public abstract <T extends Foo> T precompute();

, T, , Foo T.

( null), , .

- , , , , .

, , - , Overbose.

+4

distance(T) , -, Metric, , Foo .

, , Metric, Bar, Bar.

+2

T , .

, . :

public abstract class Metric {
    public abstract Foo precompute(); // valid syntax
    public abstract void distance(Foo arg); // valid syntax
    public class Foo {}
}

public class MetricDefault extends Metric {
    public Bar precompute() { return new Bar(); } // Valid - return type extends Foo
    public void distance(Bar arg) {} // Signature not compatible - does not override distance(Foo)
    public class Bar extends Foo {}
}
+1

@Paŭlo Ebermann,

    <T extends Foo>
    T   precompute();

    Bar precompute()

JLSv3 # 8.4.8.3 # 8.4.5 . Javac .

  • the specification is omitted by an intuitive and obvious rule: the return-replace relationship must be transitive. T->Foo->Bar, BarThe override Tis legitimate.

  • javac is defective. It somehow makes a mistake Fooas the return type of method # 1 when checking overrides.

The first explanation is completely implausible. If the (given) relation is transitive, it is too difficult to check if it holds for 2 arbitrary types. Therefore, the second explanation is probably more true.

+1
source

All Articles