Java Generic functions: Define a set of classes generic object must be - java

I'm writing a generic function in java, however, I can't seem to tell if there's any way of setting a list of classes that a Generic Object must be in.
Something like this:
public static <T in {String.class, Integer.class, Long.class}> Collection<T> test(Collection<T> val);

You can have Bounded Type Parameters like this for example:
public static <T extends String & Runnable> Collection<T> test(Collection<T> val);
But your example will not work since you have several classes and Java does not support Multiple inheritance.
Also, your examples String, Integer and Long are final so they will not possible to extend anyway.

No. You can't do that in java.
What you desire is an "OR" bound. Java allows "AND" bounding, eg:
List<T extends Runnable & Comparable<T>>
But the analogous "OR" is not supported:
List<T extends Integer | String> // doesn't compile
When you think about it, it doesn't make any sense. Use separate classes/methods for each type bound.

Unfortunately you can't do what you're trying to do in Java - if it allowed you to specify a generic from a list, you'd be able to write a function which operated either on Integers or ArrayLists, which makes no sense as they don't share common functionality. If you want to limit your method to certain types, either find a base class/interface which they all inherit from supporting the functionality you need (public static <T extends BaseClass> Collection<T> test(Collection<T> val);) or write a set of oveloaded methods, one for each type you want to support.

Well you can and can not at the same time.
In your specific example it is impossible.
But such an example would work:
public <T extends CharSequence & Comparable & Closeable> void go(){
For your case I would go with:
public <T extends Number & CharSequence> void go() // same rule applies as extend a class and implement as many interfaces as you want

You cannot do that. Generics is only for enforcing type safety (guaranteeing that the type can perform a particular method, etc.). There is no type-safety requirement that correspond to such an "OR" bound. Requirements on type safety only require an "AND" bound.

Related

Bounded type parameters

Sorry guys, this might be a naive question.
I am a little bit confused by bounded type parameter and wildcard. What's the difference between <T extends String> and <? extends String>?
Thanks
I think you are mixing up some things here.
<T extends String> is used when you declare a generic class.
<? extends String> is used on instances from classes that are already generic.
Let’s take the interface “List” from the collections framework for example:
List <E> is the same like List <E extends Object> meaning that you can use the list with every datatype that inherits from Object.
The wildcard <?> can only be used on Classes that are already generic. Taking the example from above with List<E>.Let’s say you have a method that is using the List, but you don’t want to allow every datatype that inherits from Object.
You could use a distinct datatype like:
public void myMethod(List<String> list){
//…
}
But you could also use a range of datatypes that you want to allow:
public void myMethod(List<? extends String> list{
//..
}
In the second example you could use every datatype that is covariant with string i.e. is a child of string.
Tldr:
Bounds <T extends String> are used to declare the range of datatypes a generic class is supporting.
Wildcards <? extends String> are used on classes that are already generic and restrict/limit the given datatypes to a certain range.
The second one basically means: you really don't care about the actual type. You just care that it would extend String.
In other words: if your code does not need to use "T" anywhere; then you can make your intention more clear by "not at all mentioning that type name T".
For more technical background, one of the best resources is the work of Angelika Langer.

Java Type Wildcarding

(For the purposes of this post, lets set aside java.util.Observable)
I was experimenting around with generics, and then wildcard types. The aim was to create a type-generic observable cache with deltas provided to the observers. Where this starts to go off the rails is I wanted to allow more generic observers to be used than the one specified in the Observable, e.g. Observer<Object> or some other common superclass.
I've since concluded that this is overly complex for my use case, but the problem itself continues to bother me since I clearly don't understand how to use type wildcarding properly.
So if we start with a simple observer interface:
public interface Observer<T> {
public void notifyChange(ChangeHolder<T> change);
}
And the associated ChangeHolder, in a full implementation this would be more complex, providing lists of added / updated / deleted objects, but this is sufficient to demonstrate the issue
public interface ChangeHolder<T> {
T getChange();
}
So with the Observer defined, I tried to implement the Observable abstract class:
public abstract class Observable<T> {
private Set<Observer<? super T>> observers = new HashSet<>();
public void addObserver(Observer<? super T> obs){
observers.add(obs);
}
public void change(ChangeHolder<T> changes){
for(Observer<? super T> obs : observers){
obs.notifyChange(changes);
}
}
}
And with that I could define some object caches, by declaring something like class TreeCache extends ObservableCache<Tree>, (From this point on I'll use Tree as an example class to be used as a T, assume it to be a simple POJO extending only from Object) and pass ChangeHolder<Tree> objects to TreeCache.change() when necessary. Unfortunately the compiler disagrees:
The method notifyChange(ChangeHolder<capture#2-of ? super T>) in the type Observer<capture#2-of ? super T> is not applicable for the arguments (ChangeHolder<T>)
Which is where my understanding ends.
Without the ChangeHolder class (if my notifyChange method just took a plain T instead) it works just fine since it's perfectly legal to pass a Tree to Observer.notifyChange(Object).
I inferred that I should be able to do the same with the ChangeHolder - ChangeHolder<T> should satisfy notifyChange(ChangeHolder<? super T>) in the same way that T satisfies notifyChange(? super T) but clearly I am misunderstanding something?
There is no wildcard in the signature notifyChange(ChangeHolder<T> change). Therefore the generic type of the passed argument must exactly match the generic type of the Observer instance.
Observer<? super T> means an Observer of some unknown type that is a supertype of T. Since the generic type of obs may not exactly match the generic type of changes, the notifyChange method is not applicable.
There are two possible fixes:
Change the signature to notifyChange(ChangeHolder<? extends T> change) so that the method works for subtypes.
Get rid of the wildcards everywhere, so that you have just <T> instead.
I prefer solution 1, as it is a good idea for signatures to be as general as possible.

Java: Establishing correlations between type parameters

Assume that we have two generic Java interfaces: Foo<T> and Bar<T>, of which there may be many implementations. Now, assume that we want to store one of each in a single class, both using the same value for T, but keep the exact implementations typed:
public interface FooBar<T, TFoo extends Foo<T>, TBar extends Bar<T>> {
TFoo getFoo();
TBar getBar();
}
Above, T is used for the sole purpose of enforcing that TFoo and TBar's classes use the same type parameter. Adding this type parameter to FooBar seems redundant for two reasons:
FooBar doesn't actually care about T at all.
Even if it did, T can be inferred from TFoo and TBar.
My question is therefore if there is a way to enforce conditions like this without cluttering up FooBar's list of type parameters. Having to write FooBar<String, StringFoo, StringBar> instead of the theoretically equivalent FooBar<StringFoo, StringBar> looks ugly to me.
Unfortunately, there is no better way... The compiler needs the T type to be declared in order to use it and there is no other place to declare it :
EDIT : unrelated link
If bound A is not specified first, you get a compile-time error:
class D <T extends B & A & C> { /* ... */ } // compile-time error
(extract from this doc)
And this is a little out of the subject, but this doc defines the conventions on type parameters names as being single, uppercase letters.

Error in Generics code extend number super integer?

This code has an error in it but I dont know how I can go about correcting it:
public class Point<T extends Number super Integer>{
}
Super is only valid with wildcards, not with named type parameters.
Let's imagine the compiler allowed that. There are only two types that can be said to extend Number and be a supertype of Integer, those are Number and Integer.
I'm struggling to see what benefit you would get in doing this rather than a straightforward non-generic Point type with int fields.
If the real case is more involved and you want a generic Point that could use Doubles, Integers etc., sure, use T extends Number if the Number restriction helps to avoid mistakes.
However, just having T extends Number does not give you access to +, -, *, etc. You might want the type class pattern for that, which involves having a separate dictionary of operations passed from the point where the generic type is created to where the numeric operations happen.
E.g.,
interface NumericOperations<T extends Number> {
T plus(T x, T y);
T subtract(T x, T y);
T multiply(T x, T y);
...
}
You would need to define instances of that type class, e.g., public static final NumericOperations intOperations = new NumericOper.....;
..and pass those instances around to get at plus, minus etc., within Point's methods.
public class Point<T extends Number>{
}
or this
public class Point<T extends Integer>{
}
You can't use super like that. See here: java generics super keyword
You can only use the super keyword with a wildcard.
You should take a look at the PECS principle : Provider Extends Consumer Super.
The super keyword is used in generics methods for consumer generic objects.
Example :
public void copyList(List<? extends Number> elementsToBeCopied,
List<? super Integer> listToBeFilled) {...}
Remark : Integer is a final class and cannot be extended, so the extends keyword is unnapplicable.
This article shows a good example of how super and extends can be used.

Is there any way of imitating OR in Java Generics

EDIT: I changed a bit the example for getting the idea:
Like
<Integer or Float>
...without having to create a common interface and make a subclass for Integer and Float to implement it
If not, something like this would maybe have more sense and be useful
<E extends Number> <E = (Integer|Float)>
If ? is a wildcard why should not we allowed to restrict certain types?
It's not possible and I hardly see any value in it. You use generics to restrict type, e.g. in collections. With or operator you know as much about the type as much you know about the most specific supertype of both of them, Object in this case. So why not just use Object?
Hypothetical:
List<E extends String or Number> list = //...
What is the type of list.get(0)? Is it String or Number? But you cannot have a variable of such type. It cannot be String, it cannot be Number - it can only be... Object.
UPDATE: Since you changed your example in question to:
<Integer or Float>
why won't you just say:
<Number>
? Note that Number has methods that allow you to easily extract floatValue() and intValue(). Do you really need the exact type?
Note that you can use and operator:
<E extends Serializable & Closeable>
And that makes perfect sense - you can use variable of type E where either Serializable or Closeable is needed. In other words E must extend both Serializable and Closeable. See also: Java Generics Wildcarding With Multiple Classes.
In very extreme cases (pre-Java 7 without AutoCloseable), I would have liked to be able to do that, too. E.g.
<E extends Connection or Statement or ResultSet>
That would've allowed me to call E.close(), no matter what the actual type was. In other words, E would contain the "API intersection" of all supplied types. In this case it would contain close(), and all methods from java.sql.Wrapper and java.lang.Object.
But unfortunately, you cannot do that. Instead, use method overloading, e.g.
void close(Connection c);
void close(Statement s);
void close(ResultSet r);
Or plain old instanceof
if (obj instanceof Connection) {
((Connection) obj).close();
}
else if (obj instanceof Statement) { //...
Or fix your design, as you probably shouldn't have to intersect APIs of arbitrary types anyway
I don't see a real use for it... But anyways, I believe the closest you'd get to it is extending common interfaces for the possible implementations.

Categories

Resources