Java generics parameter bounding to any of a range of types
Is there a syntax or workaround to constrain a generic type parameter to any of a range of types?
I am aware that you can constrain a type to be all of a range of types (ie AND
logic):
public class MyClass<T extends Comparable<T> & Serializable> { } // legal syntax
Is there an OR
logic version, ie something like this:
public class MyClass<T extends Comparable<T> | Serializable>开发者_如何学JAVA; { } // illegal syntax
If there isn't a syntax that supports this (I don't think there is), is there a workaround or approach that is a good pattern?
For some context, one example use case might be:
/** @return true if the obj is either has the same id, or if obj is the same as id */
public <T extends MyClass | String> boolean sameAs(T obj) {
if (obj instanceof String) return this.id.equals(obj);
if (obj instanceof MyClass) return this.id.equals(((MyClass)obj).id);
return false;
}
People seem to be getting hung up on the exact semantic of my method example above. Let's try this instead:
public class MyWrapper<T extends A | B> {
// my class can wrap A or B (unrelated classes). Yes I will probably use instanceof
}
EDITED:
I won't know at compile time which I might get (coming from external code), so I want to avoid having concrete classes for each type. Also, I have to give my class to a foreign system who invokes my class.method, but the other system can give me instances of a variety of classes, but a narrowly defined and known variety.Some people have commented on instanceof
being "impure". Well, one workaround is to use a factory method to pick my concrete class based on the class of the incoming object, but that factory method would have to use instanceof
, so you're just moving the instanceof
to another place - you still need the instanceof
.
Or is this idea just not ever a good one?
public class MyWrapper<T extends A | B> {}
You can't do this for interfaces that you don't have control over, but for your own stuff you could use an empty marker interface:
interface AOrB {
}
interface A extends AOrB {
someMethodHere();
}
interface B extends AOrB {
someOtherMethodHere();
}
public class MyClass<T extends AOrB> {}
Regardless of what purists say, using instanceof
is perfectly fine when you need it.
No. It wouldn't make any sense unless all the types had a non-empty union type, e.g. an interface they all implemented, or a base class they all extended, in which case you just specify the union type.
While Java has limited support for "intersection types" like T1 & T2
, support for "union types" is scanty.
Generic types with wildcard are actually union types: G<? extends X>
is the union of all G<S>
where S
is a subtype of X
.
No support for union of arbitrary two types.
Java 7's multi-catch syntax looks like it supports union of arbitrary exception types
catch (IOException|SQLException ex){ .. }
but not really, the type of ex
is a fixed super class, not a union of the two classes.
Using of instanceof
is considered as not very good style of programming, and allowing you to use OR
in generics implies you will use one.
The following code would do the same thing as in the provided example, but without runtime type checking and typecasts.
public boolean sameAs(MyClass obj) {
return this.id.equals(obj.id);
}
public boolean sameAs(String obj) {
return this.id.equals(obj);
}
NPE checking might be a good idea.
精彩评论