简体   繁体   中英

How to determine the return type of a method from the class it is implemented in?

I have a set of classes that are used to store data for which I am currently trying to implement a deep copy mechanism. For this, I would like to define a method deepCopy() in my abstract class which every extending class needs to implement.

I am struggling with defining the abstract method in a generic way so that every deepCopy() returns an Object of the class it was implemented in.

public abstract class Foo {
    public abstract Foo deepCopy();
}

public class Bar extends Data {
    @Override
    public Bar deepCopy() {
        //copying;
    }
}

This is what I want to achieve, but I end up with this:

public abstract class Foo {
    public abstract Foo deepCopy();
}

public class Bar extends Data {
    @Override
    public Foo deepCopy() {
        //copying;
    }
}

This causes an error when calling deepCopy() :

public SomeClass extends Foo {
    private Bar value;

    private SomeClass(final SomeClass original) {
        this.value = original.deepCopy(); // type-mismatch since deepCopy returns Foo but value is Bar.
    }

    @Override
    public Foo deepCopy() {
        return new SomeClass(this);
    }
}

The simplest way to resolve this would be to cast the returned object of deepCopy to Bar, but this seems wrong.

Is there a clean way to achieve this?

You can do this with Generics in java.

Implement the class like this.

public abstract class Foo<Item> {
    public abstract Item deepCopy();
}

You can read about generics here http://docs.oracle.com/javase/tutorial/java/generics/

It would seem that covariant return types would solve the issue here:

public abstract class Foo {
    public abstract Foo deepCopy();
}

Then Bar can return a Bar from the method:

public class Bar extends Foo {
    @Override
    public Bar deepCopy() {
        //copying;
    }
}

So:

final Bar bar = new Bar();
final Bar barCopy = bar.deepCopy(); // <- Bar's deepcopy returns a Bar
final Foo foo = ((Foo) bar).deepCopy(); // <- Foo's deepcopy returns a Foo

The is allowed because as Bar extends Foo you can use a Bar wherever you would be allowed to use a Foo . So a subclass can override methods from a superclass that return covariant types.

This would work in the more general case:

public abstract class Foo {
    public abstract Number getThing();
}

public class Bar extends Foo {
    @Override
    public Integer getThing() {
        //copying;
    }
}

As you can see, Bar.getThing returns an Integer but it overrides the method Foo.getThing which returns a Number . This is fine because we can use Integer wherever we can use Number .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM