Confused with generics and subtyping - java

So, let's say we have a simple interface
public interface ICopyable<T> {
void copyFrom(T original);
}
Which basically adds a copyFrom method with a parameterized type. And you have an interface extending it:
public interface ISomeObject<T> extends ICopyable<T> {
String getType();
}
The interface isn't adding anything valuable of course apart of it's type, but let's pretend there are some useful methods in it. The idea is still the same - all objects of this interface have a getType() method and they can copy one object of THE SAME TYPE from one to the other.
Now let's have two implementations of this interface, and the second implementation inherits from the first one:
public static class ActualObject1 implements ISomeObject<ActualObject1> {
Object data1;
#Override public void copyFrom(final ActualObject1 original) {
this.data1 = original.data1;
}
#Override public String getType() {
return this.getClass().getSimpleName();
}
}
public static class ActualObject2 extends ActualObject1 {
Object data2;
#Override public void copyFrom(final ActualObject1 original) {
super.copyFrom(original);
// oh no! i've just realized that i'm not copying the ActualObject2!
}
}
So the second's object(ActualObject2) is supposed to extend the ActualObject1, but if it's done this way it can't implement the right "copyFrom" method, as the first class implements the interface ISomeObject for itself only. And it obviously wants to do it somehow to allow copying of ActualObject2's to each other. But how?
It can't just declare implements ISomeObject as it'll clash with it's parent's implementation type.
So you'll want to do something like that maybe?
public static class ActualObject1<T extends ActualObject1> implements ISomeObject<T> {
Object data1;
#Override public void copyFrom(final ActualObject1 original) {
this.data1 = original.data1;
}
#Override public String getType() {
return this.getClass().getSimpleName();
}
}
public static class ActualObject2 extends ActualObject1<ActualObject2> {
Object data2;
#Override public void copyFrom(final ActualObject2 original) {
super.copyFrom(original);
this.data2 = original.data2;
}
}
Basically parameterizing the class1, and class2 specifies itself as a parameter. It all works fine, you can create instances of both types:
ActualObject1 obj1 = new ActualObject1();
However there's one "little" problem - the obj1 has a raw type. The full declaration looks rather silly:
ActualObject1<ActualObject1> obj2 = new ActualObject1<>();
But it works. However the "raw type" nature of this class can bite for example in this scanario:
public static class SomeOtherParameterizedClass<T extends ISomeObject<T>> {
void copyObjects(T obj1, T obj2) {
obj1.copyFrom(obj2);
}
}
So you're creating some random class parameterized by <T extends ISomeObject<T>>. And you can in theory say it like that: <T extends ISomeObject> but then you won't be able to use T in the "copyFrom" safely. In other words - it's a valid class parameterization, it has a point.
But then you can't parameterize it for ActualObject1:
SomeOtherParameterizedClass<ActualObject1> a1 = new SomeOtherParameterizedClass<>();
Yea - doesn't work. Hint:
SomeOtherParameterizedClass<ActualObject2> a2 = new SomeOtherParameterizedClass<>();
Works just fine...
So what's the right approach here? I'm more interested in retaining the type safety as much as possible, as for sure you can just use raw types all the way and don't worry about anything, but it's for the weak! :-)
We're in statically typed language so it's somewhat of an academic question - what's the right way of designing this class hierarchies with generics? Is occasionally using raw-types actually required for those to work?

Related

Can acceptable type be restricted for a generic method in java?

I have a similar requirement to this question.
I would like to generify a method but restrict the types the acceptable by the generic parameter. Currently what I do is attempt to cast to the acceptable types in the method but seems cumbersome if dealing with more than 2 or 3 types.
EDIT:
The types may not be of the same base class. Apologies for not mentioning this earlier.
For this, You must have a base class so that you can do this.
public class Person {
String name;
List<Profession> professions;
int age;
}
public class Doctor {
String university;
Boolean doctorate;
public void work() {
// do work
}
}
public class Teacher {
List<Grade> grades;
float salary;
public void work() {
// do work
}
}
public class Animal<T> {
T type;
}
So, now if you want to write a method which is generic and applies to all, You can do something like this,
public void doSomething(Animal<T extends Person> human) {
human.work();
}
If the class is not of type Person, it will show a compilation error.
UPD1:
In the case, all the classes do not have a common base class. There is some functionality that makes them unique. By this, we can consider them to have a common function, which we can and should add using an interface.
Let's look at some code,
public class Human implements Growable {
public void grow() {
// human grow code
}
}
public class Plant implements Growable {
public void grow() {
// plant grow code
}
}
public class Table {
// does not grows
}
public class GrowService {
public static void grow(Growable growable) {
growable.grow();
}
}
interface Growable {
public void grow();
}
And by calling the below method, we can achieve this
// Works fine
GrowingService.grow(new Plant());
// throws compilation error
GrowingService.grow(new Table());
Java Generics allow basic wildcards such as <T> but also more specifics like
<T extends Number> which means any type T that is Number or a subclass of it or
<T super Number> which means T can be Number or any superclass of Number all the way up to Object.

Java Inheritance without casting

public class BinaryVertex {
public BinaryVertex parent,left,right;
}
public class BSTVertex extends BinaryVertex {
public void foo() {
left = new BSTVertex();
if(Math.floor(Math.random()*2) == 0) left.foo();
}
}
I'm making a tree / graph api for school, approaching it from a oop standpoint. But im trying to figure out a way for the inherited class to treat some of its base class variables as its own type (i.e. parent,left,right should be treated as BSTVertex when called from BSTVertex but treated as BinaryVertex when called from BinaryVertex) without casting.
I'm thinking of generics but I'm not sure how to implement that in this situation.
UPDATE
Nice, didnt know you could use extend in generics. But I'm getting a BSTVertex<T> cannot be converted to T error with this:
public class Test {
public static void main(String[] args) {
new AVLVertex();
BSTVertex<BSTVertex> v = new BSTVertex<BSTVertex>();
v.foo();
}
class BinaryVertex<T extends BinaryVertex> {
public T parent, left, right;
}
class BSTVertex<T extends BSTVertex> extends BinaryVertex<T> {
public T foo() {
return this; //error here
}
}
class AVLVertex extends BSTVertex<AVLVertex> {
// this might probably end up being abstract too
}
foo needs to return a vertex of the same type as caller, i.e. if AVLVertex calls foo its expecting to get AVLVertex not BSTVertex
Yes, you can use generics like this:
public class BinaryVertex<T extends BinaryVertex<T>> {
public T parent, left, right;
}
public class BSTVertex extends BinaryVertex<BSTVertex> {
public void foo() {
left = new BSTVertex();
if(Math.floor(Math.random()*2) == 0) left.foo();
}
}
The same way the Comparable interface implemented, so subclasses receive the same type to compareTo method. For example, Integer implements Comparable<Integer>, so its compareTo method receives Integer argument.
Also please note the it would be better to create your own random number generator like this:
public class BSTVertex extends BinaryVertex<BSTVertex> {
private static final Random r = new Random();
public void foo() {
left = new BSTVertex();
if(r.nextBoolean()) left.foo();
}
}
UPDATE
In your updated code (in future please ask new question instead) you cannot safely cast, because you can potentially write later:
class RBVertex extends BSTVertex<RBVertex>{}
class AVLVertex extends BSTVertex<RBVertex>{}
This is ok from the compiler's point of view, but your AVLVertex generic argument is actually not an AVLVertex. That's why you have a compilation error in foo() method: your class can be later possibly extended in the way that would make your T incompatible with this.
You can fix this problem by doing an unchecked cast:
#SuppressWarnings("unchecked")
public T foo() {
return (T) this;
}
In this way if you mistakenly create class AVLVertex extends BSTVertex<RBVertex>{}, it will still compile, but upon calling AVLVertex.foo() you may have a runtime ClassCastException.

Java Generic Setter with Interface and Abstract Class

i have a problem with an Interface and generic setters that i tried to solve for some hours now.
i have an interface where i want to define some getter and setter functions. the getter functions should be implemented by some abstract class since they usually shouldn't change.
the setter functions on the other hand should be overrideable multiple times by a defined class. In the case i try to describe it would be that the childClass should be able to implement 2 setFunctions with the same name and different input values
Interface TestClass {
public abstract void setSomething(List<?> value);
public abstract List<String> getSomething();
}
abstract class AbstractTestClass implements TestClass {
List<String> someData;
public List<String> getSomething() {
return someData;
}
}
class TestClassImplementation extends AbstractTestClass() {
#Override
public void setSomething(List<String> data) {
someData = data;
}
#Override
public void setSomething(List<SomeOtherType> data) {
someData = convertToStringList(data);
}
private List<String> convertToStringList(List<SomeOtherType> data) {
... do some conversion ...
return returnList;
}
}
hope this gives the idea of what i want to do. I would even prefer to implement the setSomething with the stringlist in the abstract class. But both setters must be reachable.
Thanks
You simply can't do that. Generics are not retained at runtime (google type erasure for more infos on this or just read the wikipedia page).
This means that your setSomething-methods all have the same signature, as their only parameter is of type List.
Well, you should try with generic solution:
Interface TestClass<T> { //Generic type T that you will provide when extending with actual class
public abstract void setSomething(List<T> value);
public abstract List<T> getSomething();
}
class TestClassImplementation extends AbstractTestClass<RealType> {
#Override
public void setSomething(List<RealType> data) {
someData = data;
}
}
The thing that confuses you is that the wildcard sign ? does not mean it changes any type, it just denotes an unknown type.

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);
}
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.
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?
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"?
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.

Avoiding Java Type Erasure

Is there a way one could avoid type erasure and get access to a type parameter?
public class Foo<T extends Enum<?> & Bar> {
public Foo() {
// access the template class here?
// i.e. :
baz(T.class); // obviously doesn't work
}
private void baz(Class<T> qux) {
// do stuff like
T[] constants = qux.getEnumConstants();
...
}
}
I need to know about T, and do things with it. Is it possible, and if so, how can it be done without passing in the class in the constructor or anywhere besides the parameter?
EDIT: The main purpose of this question is to find out if there is any practical way around type erasure.
AFACT, there is no practical way around type erasure because you can't ask for something which the runtime doesn't have access to. Assuming of course you agree that sub-classing generic classes for each enum which implements Bar interface is a practical work around.
enum Test implements Bar {
ONE, TWO
}
class Foo<T> extends FooAbstract<Test> {
public Foo() {
ParameterizedType genericSuperclass =
(ParameterizedType) getClass().getGenericSuperclass();
baz((Class<T>) genericSuperclass.getActualTypeArguments()[0]);
}
private void baz(Class<T> qux) {
T[] constants = qux.getEnumConstants();
System.out.println(Arrays.toString(constants)); // print [ONE, TWO]
}
}
interface Bar {
}
class FooAbstract<T extends Enum<?> & Bar> {
}
If you're willing/able to hand a class token to the constructor:
public Foo(Class<T> clazz) {
baz(clazz);
}
private void baz(Class<T> qux) {
// ...
}
That way, you can produce objects of type T with Class.newInstance(), attempt to cast arbitrary objects to T using Class.cast(), etc.
What do you intend to do in baz()?
As pholser points out in his answer, the only way to achieve this is by passing in the Class object representing the type T. It's because of Type Erasure that something like T.class isn't possible because T is erased before runtime.
You seem resistant against passing in the Class object, but it's the only way to use the method getEnumConstants(). Here is a self contained example:
public class Foo<T extends Enum<?> & Bar> {
final Class<T> clazz;
public Foo(Class<T> clazz) {
this.clazz = clazz;
}
public void baz() {
T[] constants = clazz.getEnumConstants();
System.out.println(Arrays.toString(constants));
}
public static void main(String[] args) {
new Foo<MyEnum>(MyEnum.class).baz(); //prints "[A, B, C, D]"
}
}
public interface Bar { }
public enum MyEnum implements Bar { A, B, C, D; }
Use a super type token as proposed by Neil Gafter and used by libraries like guice for this purpose.
See http://gafter.blogspot.com/2006/12/super-type-tokens.html for the original description and I've check out the guice source for CA radio life working implementation.
there is another q which has an answer with worked example inline here How can I pass a Class as parameter and return a generic collection in Java?
In some cases you can use a workaround suggested by Richard Gomes.
When creating instances of anonymous classes, the type parameter class info is available.
class A<T>
{
A()
{
java.lang.reflect.ParameterizedType parameterizedType = (java.lang.reflect.ParameterizedType) (this.getClass().getGenericSuperclass());
System.out.println(parameterizedType.getActualTypeArguments()[0]);
}
}
public class Example {
public static void main(String[] args) {
A<String> anonymous = new A<String>() {}; // prints java.lang.String
}
}
Note that multiple instances created this way will be of different anonymous classes, and if that's a problem you might want a class A_String_Factory with a createNew() function based on clone to replace the calls to new.

Categories

Resources