Question

I am reading jls and I faced with following term:

return-type-substitutable

snippet from jls

A method declaration d1 with return type R1 is return-type-substitutable for another method d2 with return type R2 iff any of the following is true:

If R1 is void then R2 is void.

If R1 is a primitive type then R2 is identical to R1.

If R1 is a reference type then one of the following is true:

--R1, adapted to the type parameters of d2 (§8.4.4), is a subtype of R2.

--R1 can be converted to a subtype of R2 by unchecked conversion (§5.1.9).

--d1 does not have the same signature as d2 (§8.4.2), and R1 = |R2|.

first two points is clearly.

can you clarify

--R1, adapted to the type parameters of d2 (§8.4.4), is a subtype of R2.

--R1 can be converted to a subtype of R2 by unchecked conversion (§5.1.9).

--d1 does not have the same signature as d2 (§8.4.2), and R1 = |R2|.

thanks

P.S. for Luiggi Mendoza

interface Foo {
        List<String> foo(String arg1, String arg2);
}

class Bar implements Foo {
    @Override
    public ArrayList<String> foo(String arg1, String arg2) {
        //implementation...
        return  null;
    }

    public String foo(String arg1, String arg2, String arg3) {
        //implementation...
        return  null;
    }
}

it is working variant.

Cause of my question - I want to inderstand following phrase from jls:

If a method declaration d1 with return type R1 overrides or hides the declaration of another method d2 with return type R2, then d1 must be return-type-substitutable (§8.4.5) for d2, or a compile-time error occurs

rule:

If R1 is a reference type then **one of the following** is true:
...
--d1 does not have the same signature as d2 (§8.4.2), and R1 = |R2|.
...

code:

interface Foo {
        List<String> foo(String arg1, String arg2);
}

class Bar implements Foo {
    @Override
    public List<String> anotherName(String arg1, String arg2,Object obj) {
           return  null;
    }

it is compile error.

R1==R2 ( List<String > == List<String>)

d1!=d2

Where have I broken the rule?

Was it helpful?

Solution

Let's have this interface

interface Foo {
    List<String> foo(String arg1, String arg2);
}

And a class implementing it

class Bar implements Foo {
    @Override
    public List<String> foo(String arg1, String arg2) {
        //implementation...
    }
}

We have:

  • Bar#foo as d1
  • List<String> return type in d1 as R1.
  • Foo#foo as d2
  • List<String> return type in d2 as R2.

R1, adapted to the type parameters of d2 (§8.4.4), is a subtype of R2.

It means that R1 can be a subtype of R2, which means R1 should pass the IS-A test. So, we can do the following:

class Bar implements Foo {
    @Override
    public ArrayList<String> foo(String arg1, String arg2) {
        //implementation...
    }
}

R1 can be converted to a subtype of R2 by unchecked conversion (§5.1.9).

This is more related to generics. It means that R1 should pass the IS-A test even if it throws a warning for unchecked overriding. So, we can do the following:

class Bar implements Foo {
    @Override
    public ArrayList foo(String arg1, String arg2) {
        //implementation...
    }
}

d1 does not have the same signature as d2 (§8.4.2), and R1 = |R2|

This means overloading:

class Bar implements Foo {
    @Override
    public ArrayList<String> foo(String arg1, String arg2) {
        //implementation...
    }

    public ArrayList<String> foo(String arg1, String arg2, String arg3) {
        //implementation...
    }
}

OTHER TIPS

If a method declaration d1 with return type R1 overrides or hides the declaration of another method d2 with return type R2, then d1 must be return-type-substitutable (§8.4.5) for d2, or a compile-time error occurs as d2 (§8.4.2), and R1 = |R2|.

Let's break this in pieces:

First, we need a method declaration d2 and a return type R2:

class SomeClass {
    public List<String> d2() {
        return null;
    }
}

Now, we define a class extending SomeClass that defines d1 and returns R1.

class AnotherClass extends SomeClass {
    //@Override annotation means it is overriding a method in parent class
    //d2 here is d1
    //Object here is R1
    @Override
    public List<String> d2() {
    }
}

How can this compile? Because d1 is return-type-substitutable for d2, which means R1 can replace R2 as return type. This is also called covariant return type and is covered in Java Tutorials. Returning a Value from a Method. This is supported by the other rule:

A method declaration d1 with return type R1 is return-type-substitutable for another method d2 with return type R2 if any of the following is true: If R1 is a reference type then one of the following is true: d1 does not have the same signature as d2 (§8.4.2), and R1 = |R2|.

Which means all of these methods are valid for overriding SomeClass#d2:

class AnotherClass extends SomeClass {
    //ArrayList implements List
    @Override
    public ArrayList<String> d2() {
        return null;
    }
}

class AnotherClass extends SomeClass {
    //raw List can be casted to `List<String>` by unchecked conversion
    //means you don't need a explicit cast to convert `List` to `List<String>`
    //due to type erasure
    @Override
    public List d2() {
        return null;
    }
}

class AnotherClass extends SomeClass {
    //combination of both examples above
    @Override
    public ArrayList d2() {
        return null;
    }
}

But these are invalid implementations:

class AnotherClass extends SomeClass {
    //the signature is not the same
    @Override
    public List<String> d1() {
        return null;
    }
}

class AnotherClass extends SomeClass {
    //Set is not a subtype of List
    @Override
    public Set<String> d2() {
        return null;
    }
}

class AnotherClass extends SomeClass {
    //Collection is not a subtype of List
    @Override
    public Collection<String> d2() {
        return null;
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top