The first thing I would point out is that you can't directly define a class which only accepts a specific list of types. You can only bound T, say, to be a subclass of 1 type (or intersection of types).
For the types listed in the question, the only common supertypes of these is Serializable
and Object
. You could declare the type variable as:
class Kubus<T extends Serializable> { // or Kubus<T>, equivalent to T extends Object
but you can then instantiate with any T
within this bound, e.g. Kubus<ArrayList<Frobnicator>>
, since ArrayList
implements Serializable
. (Serializable
is also a pretty useless type to bound on, unless you actually want to serialize Kubus
using built-in serialization. To paraphrase Josh Bloch's advice in Effective Java: "don't").
If you want it only to accept those types, all you can do is to limit how the class can be constructed, by making the constructor private, and then providing static factory methods for the types you want:
private Kubus(T tinggi, T panjang,T lebar ) { ... }
public static Kubus<Integer> ofInteger(Integer tinggi, Integer panjang, Integer lebar ) {
return new Kubus<>(tinggi, panjang, lebar);
}
public static Kubus<Long> ofLong(Long tinggi, Long panjang, Long lebar ) {
return new Kubus<>(tinggi, panjang, lebar);
}
// etc for other specific types.
You can then create instances with
Kubus<Integer> first = Kubus.ofInteger(1, 2, 3);
Kubus<Long> second = Kubus.ofLong(1L, 2L, 3L);
It is still valid to write the type Kubus<ArrayList<Frobnicator>>
(or whatever arbitrary type), but you just can't create an instance of it.
Kubus<ArrayList<Frobnicator>> whatever = null;
This actually gives you the opportunity to solve the specific problem that you asked about (*).
The issue is that you can't multiply arbitrary things together. Here, you need some means to convert a "thing" to a long
in order to multiply it. Casting to Long
is not a safe choice, because you can't cast, say, an Integer
or a String
to a Long
.
Because you want to restrict T
to only certain types, you can also provide a converter to do the conversion type-safely.
Such a converter can be modelled using a ToLongFuntion<T>
: this takes a T
, and returns a long
. So, change your constructor to take another parameter:
private Kubus(T tinggi, T panjang,T lebar, ToLongFunction<T> converter ) { ... }
public static Kubus<Integer> ofInteger(Integer tinggi, Integer panjang, Integer lebar ) {
return new Kubus<>(tinggi, panjang, lebar, i -> i.intValue());
}
public static Kubus<Long> ofLong(Long tinggi, Long panjang, Long lebar ) {
return new Kubus<>(tinggi, panjang, lebar, lng -> lng.longValue());
}
// etc for other specific types.
Assign converter
to a field in the constructor, and then apply in your multiplication method to get the value as a long, which can be multiplied:
return converter.applyAsLong(getTinggi())
* converter.applyAsLong(getPanjang())
* converter.applyAsLong(getLebar());
(*) I am not intending to imply that you can only use this converter
approach with a private
constructor: you can, of course, make the constructor non-private, and instantiate directly using the constructor. I just suspect that you'd very quickly get tired of the repetition in writing:
Kubus<Integer> first = new Kubus<>(1, 2, 3, Integer::longValue);
Kubus<Integer> second = new Kubus<>(1, 2, 3, Integer::longValue);
Kubus<Integer> third = new Kubus<>(1, 2, 3, Integer::longValue);
// ...