Reputation: 425348
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> { } // 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?
Upvotes: 20
Views: 8919
Reputation: 45453
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.
Upvotes: 4
Reputation: 311050
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.
Upvotes: 7
Reputation: 36577
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.
Upvotes: 9
Reputation: 2415
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.
Upvotes: 0
Reputation: 5410
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.
Upvotes: 0