multiholle
multiholle

Reputation: 3160

Create generic Interface restricted to own class

I would like to create a generic interface for those two classes but I'm not sure how to specify the generics the right way.

public class ThingA implements Thing {
    public ThingA createCopy(ThingA original);
}

public class ThingB implements Thing {
    public ThingB createCopy(ThingB original);
}

I tried it this.

public interface Thing<V extends Thing<V>> {
    public V createCopy(V original);

}

But I'm still able to do things like this, which shouldn't be allowed.

public class ThingB implements Thing<ThingA> {
    public ThingA createCopy(ThingA original);
}

Upvotes: 7

Views: 992

Answers (4)

scriber36
scriber36

Reputation: 135

In case you are free to use extension instead of implementation, then you could do that this way:

public interface Thing { ... }
public abstract class Copyable {
    public final Copyable copy() {
        Copyable clone = createCopy();
        if (clone.getClass() != getClass())
            throw new RuntimeException("Copy has wrong type!");
        return clone;
    }
    protected abstract Copyable createCopy();
}

And then use it like:

public class Test extends Copyable implements Thing {
    public String content = null;

    @Override
    public Copyable createCopy() {
        Test clone = new Test();
        clone.content = this.content;
        return clone;
    }
}

/*code somewhere*/ {
    Test t1 = new Test();
    t1.content = "Hello world!";
    Test t2 = (Test)t1.copy();
    System.out.println(t2.content);
}

One problem with this, is that Copyable is not an interface. However, this can be used without much pain, as seen in the example, but the class checking used is not supported on the language level. With other words, the createCopy abstract method is not restricted to the class it copies, and all that is up to the programmer who extends the Copyable class, or a class, which extends it.

The positive side, is that if you call the .copy() on the object, it must return an object same as itself. Instead of an exception you can return null, if you want. Then you got good or nothing.

But, to be honest, I don't really understand, why your createCopy local method has a parameter. It could be then a static method ... altrough I cannot even imagine what would go into that code block:

static <X extends Thing> X copy(X object) { ... }

May you could combine the pratice with a static generic method and the result becomes a bit more friendly:

public interface Thing extends Cloneable {
    public static <X extends Thing> X copy(X thing) {
        Object clone = thing.clone();
        if (clone.getClass() != getClass())
            throw new RuntimeException("Copy has wrong type!");
        return (X)clone;
    }
}

public class ThingA implements Thing {
    public Object clone() { ... }
}

/*code somewhere*/ {
    ThingA a1 = new ThingA();
    ThingA a2 = Thing.copy(a1);
}

Still, the cloning method is regulated by an exception instead of language restriction, but I think this is far the best solution.

Upvotes: 0

newacct
newacct

Reputation: 122439

This is not possible. And it is not what Generics is for. Generics is for type safety, i.e. avoiding casts. If someone makes a class ThingB that implements Thing<ThingA> somehow, then great. It is perfectly type-safe. Why do you care? How does it impede what you are doing?

Upvotes: 2

Guillaume
Guillaume

Reputation: 5555

There is no this key-word generics (nor for methods parameters and return values declaration) and thus you cannot do exactly what you want.

In other words the interface will permit to ensure all the methods in the class use consistent types, but not to reference the class type itself.

Upvotes: 6

Eric Stein
Eric Stein

Reputation: 13672

Are you looking for

public interface Thing<V> {
    public V createCopy(V original);
}

? If not, can you explain in more detail what it means to you to "create a generic interface for two classes"?

Upvotes: 0

Related Questions