Ian Campbell
Ian Campbell

Reputation: 2748

Is it possible to initialize a generic variable with a specific-typed value?


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

Answers (2)

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

bstempi
bstempi

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

Related Questions