Reputation: 2748
I have a class that I want to be instantiated with either a String
or int
value, and I define the corresponding instance variable value
as generic type T
:
public class MathValue<T extends Object> {
private boolean isOperand, isOperator;
// a generic-typed instance variable:
private T value;
// constructor:
public MathValue(int operand) {
// compile-error -- "Incompatible types: required T, found Integer"
this.value = new Integer(operand);
this.isOperand = true;
this.isOperator = false;
}
// constructor:
public MathValue(String operator) {
// compile-error -- "Incompatible types: required T, found String"
this.value = operand;
this.isOperand = false;
this.isOperator = true;
}
}
I could very well have a single constructor instead, with a formal parameter of type T
, but I want to enforce the class' instantiation with a String
or int
argument:
public class MathValue<T extends Object> {
private boolean isOperand, isOperator;
// a generic-typed instance variable:
private T value;
// it totally works, but does not enforce specific-typed instantiation:
public MathValue(T operandOrOperator) {
this.value = operandOrOperator;
if (operandOrOperator instanceof Integer) {
this.isOperand = true;
this.isOperator = false;
} else if (operandOrOperator instanceof String) {
this.isOperand = false;
this.isOperator = true;
}
}
}
So despite the logical error of wanting to make a generic-typed class "not so generic", is it possible to instantiate a generic variable with a specific-typed value?
Upvotes: 1
Views: 3770
Reputation: 3412
You could use sub-classes. Define an abstract class to actually store the value, generically:
abstract class MathValue<T> {
private final T value;
MathValue(T value) {
this.value = value;
}
abstract boolean isOperator();
boolean isOperand() {
return !isOperator();
}
}
Then one subclass that enforces the value type to be an integer.
class OperandValue extends MathValue<Integer> {
OperandValue(int operand) {
super(new Integer(operand));
}
@Override
public boolean isOperator() {
return false;
}
}
and another subtype that enforces it to be a String.
class OperatorValue extends MathValue<String> {
OperatorValue(String operator) {
super(operator);
}
@Override
boolean isOperator() {
return true;
}
}
With this design, you don't actually need to store the Booleans.
(Note, for simplicity I left out the visibility keywords.)
Upvotes: 1
Reputation: 2043
You could create factory methods. Here's now it might look:
public class MathValue<T extends Object> {
public static MathValue<String> from(String s) {
MathValue<String> mv = new MathValue<String>();
mv.setValue(s);
mv.setIsOperand(true);
return mv;
}
public static MathValue<Integer> from(Integer s) {
MathValue<Integer> mv = new MathValue<Integer>();
mv.setValue(i);
mv.setIsOperand(false);
return mv;
}
// Rest of your class below
}
If you absolutely need a constructor (eg, you don't know the type that you're creating for ahead of time), then I can't see a way around @RohitJain 's suggestion.
Upvotes: 1