4

Is it possible in Java to limit a generic type <T> to only some types like:

  • Boolean
  • Integer
  • Long
  • Float
  • String

?

Edit: My problem is to limit a generic type <T> to different class types which have not common direct superclass.


Edit: I finally use the scheme proposed by Reimeus:

public class Data<T> {

    private T value;

    private Data(T value) {
        this.set(value);
    }

    public static Data<Integer> newInstance(Integer value) {
        return new Data<Integer>(value);
    }

    public static Data<Float> newInstance(Float value) {
        return new Data<Float>(value);
    }

    public static Data<String> newInstance(String value) {
        return new Data<String>(value);
    }

    public T get() {
        return this.value;
    }

    public void set(T value) {
        this.value = value;
    }
}

and:

Data<Integer> data = Data.newInstance(10);

Edit: Another approach:

public class Data<T> {

    private T value;

    private Data(T value) {
        this.set(value);
    }

    @SuppressWarnings("unchecked")
    public Data(Integer value) {
        this((T) value);
    }

    @SuppressWarnings("unchecked")
    public Data(Float value) {
        this((T) value);
    }

    @SuppressWarnings("unchecked")
    public Data(String value) {
        this((T) value);
    }

    public T get() {
        return this.value;
    }

    public void set(T value) {
        this.value = value;
    }
}

But I have a problem with it:

If by mistake the data instance is declared with:

Data<Integer> data = new Data<Integer>(3.6f); // Float instead of Integer

There is no class cast exception and data.get() returns 3.6

I don't understand why...

So, the first solution seems better.

alex
  • 5,153
  • 6
  • 30
  • 52
  • 2
    You can restrict it to a certain type they all have in common. In this case the only supertype they have in common is `Object` (and `Serializable`) which is the standard one for `` (aka: there is no need to explicitly define that here). – Jeroen Vannevel May 30 '14 at 15:15
  • 1
    I voted to close this post because the only thing to say is "Yes". What is your actual problem? Please edit the post with the scenario you're encountering. If you are just asking general questions, read this: http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html – durron597 May 30 '14 at 15:16
  • 1
    @alex Regarding your last edit: [type erasure](http://stackoverflow.com/questions/339699). The Factory scheme in Reimeus' answer eliminates that by explicitly calling the constructor in the static methods *only*. If you want to do that with constructors you have to explicitly check `instanceof`, unfortunately, the compiler doesn't enforce generic type safety. – blgt May 30 '14 at 21:16

3 Answers3

6

You can restrict it by polymorphism, eg:

<T super X> //Matches X and all superclasses of X

<T extends X> //Matches X and all subclasses of X

However, you can't just restrict it to a list of arbitrary types that are inherently unrelated.

Michael Berry
  • 61,291
  • 17
  • 134
  • 188
2

Its not possible since they all descend from different parent classes (some directly descend from Object and some from Number). Although you can restrict a generic type to only

Integer
Long
Float

and other child classes of Number by saying <T extends Number>

PS: For your requirement <T extends Serializable> will work, but this will also accept all the other classes which implement Serializable (Thanks to @Jeroen Vannevel's comment)

sanbhat
  • 16,864
  • 6
  • 46
  • 62
2

Not possible using a single declared type <T> but you could define a wrapper class that provides factory methods for the required type

public class Restricted<T> {

    private T value;

    public Restricted(T value) {
        this.value = value;
    }

    public static Restricted<Boolean> getBoolean(Boolean value) {
        return new Restricted<Boolean>(value);
    }

    public static Restricted<Integer> getInteger(Integer value) {
        return new Restricted<Integer>(value);
    }

    public static Restricted<Double> getLong(Double value) {
        return new Restricted<Double>(value);
    }

    // remaining methods omitted
}
Reimeus
  • 152,723
  • 12
  • 195
  • 261
  • Interesting! I have to test it. – alex May 30 '14 at 15:23
  • Can I write a public constructor for each type which use a private generic type constructor? – alex May 30 '14 at 15:36
  • No I can't "Recursive constructor invocation" – alex May 30 '14 at 15:40
  • @alex technically its possible to create multiple constructors per type but sounds like a poor design & maintenance nightmare :) – Reimeus May 30 '14 at 15:48
  • You are absolutely right, but in my case I need to constrain to only this finite list types, my generic class used API's methods named `getInt`, `getFloat`, `getString`... (methods are finite too) – alex May 30 '14 at 15:55
  • @Reimeus What do you have against multiple constructors? Put all the common stuff in a `private` one, and make all public ones accept concrete typed params that they forward to `this((T)value);`. (@alex cast to the generic type to avoid the compiler error) – blgt May 30 '14 at 19:26
  • @blgt I tried this solution but I have a problem with it... see my last question edit – alex May 30 '14 at 20:56