Dónal
Dónal

Reputation: 187379

Java formal type parameter definition (Generics)

I'd like to define a generic type, whose actual type parameter can only be

  1. One of the numeric primitive wrapper classes (Long, Integer, Float, Double)
  2. String

I can meet the first requirement with a definition like this

public final class MyClass<T extends Number> {
    // Implementation omitted
}

But I can't figure out how to meet both of them. I suspect this is not actually possible, because AFAIK there's no way to specify "or" semantics when defining a formal type parameter, though you can specify "and" semantics using a definition such as

public final class MyClass<T extends Runnable & Serializable > {
    // Implementation omitted
}

Cheers, Don

Upvotes: 8

Views: 6987

Answers (5)

Bartosz Klimek
Bartosz Klimek

Reputation: 565

Maybe you could do what follows:

  1. Make MyClass<T> a package-default class, invisible to other components, or at least with a package-default ctors only, so that it cannot extended or instantiated outside the package.
  2. Create two public classes in the package of MyClass<T>:
MyNumericClass<T extends Number> extends MyClass<T>
MyStringClass extends MyClass<String>

This way all subclasses of MyClass will be limited to those parametrized with a Number subclass or String.

Upvotes: 0

Markus
Markus

Reputation: 384

You could use factory methods for all supported types and make the constructor private/protected. You have to fix the generic type in the constructor anyway so that it makes sense, so you could probably code it like this:

public final class MyClass<T> {
    public static MyClass<Integer> newInstance(int i) {
        return new MyClass<Integer>(i);
    }
    public static MyClass<String> newInstance(String s) {
        return new MyClass<String>(s);
    }
    //More factory methods...

    protected MyClass(T obj) {
        //...
    }
}

Or if you do not want the constructor parameter, something like this: public final class MyClass { public static MyClass newIntegerInstance() { return new MyClass(); } //... }

As erickson stated, the common implementation can rely only on Object anyway, so the only restriction is, that you can create other implementations for other types besides the primitive and String.

Upvotes: 4

erickson
erickson

Reputation: 269797

While generics won't work here, a base type with derived types for Number and String will. Since a generic type would have erased to Object anyway, any functionality you would have put there can go in an abstract base class. You're likely to need only a type-specific accessor on the subclass to get the value.

Also, be careful with the Number class. It's not limited to boxing the primitive types, as anyone can extend it—e.g., BigInteger.

Upvotes: 2

David L
David L

Reputation: 44667

Java generics does not support union types (this parameter can be A OR B).

On a related note that may be of interest to some, it does support multiple bounds, if you want to enforce multiple restrictions. Here's an example from the JDK mentioned in the Java generics tutorial:

public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll)

Upvotes: 11

Yuval Adam
Yuval Adam

Reputation: 165282

Interesting question, it boggled me a bit. However apparently this is impossible. I tried several different hacks, none really work.

Upvotes: 0

Related Questions