问题
BACKGROUND:
I am trying to implement a tiny template, i.e. generic class, which would allow me to achieve a pass-by-reference functionality as follows.
public static class Ref<T> {
T value;
public Ref(T InitValue) { this.set(InitValue); }
public void set(T Value) { this.value = Value; }
public T get() { return this.value; }
}
So, I could define a function that takes a 'Ref' where the value can actually be changed, e.g.
public static void function(Ref<Byte> x)
{
x.set((byte)0x7E);
}
The initialization of the variable to be passed by reference looks not so elegant.
Ref<Byte> to_be_changed = new Ref<Byte>((byte)0);
...
function(to_be_changed);
...
Byte result = to_be_changed.get()
QUESTION:
Is there a way in Java to do it better? Can the constructor initialize directly a '0' according to the primitive type related to the wrapper type which is passed as template type? I.e something like
...
public Ref() { this.value = (T.relatedPrimitiveClass())0; }
...
where Integer.relatedPrimitiveClass()
shall deliver int
; Byte.relatedPrimitiveClass()
delivers byte
.
回答1:
First and the most important thing to understand is that java generics are not templates. Generics are classes/interfaces that are parameterized over types. I recommend reading generics tutorial from oracle: https://docs.oracle.com/javase/tutorial/java/generics/types.html
It is possible to use reflection to get the parametrized type T of your Ref class and use that to determinate the initial value for default construtor, but I would not recommend doing so.
Instead of reflection you can create subclasses for types, that require default constructors (e.g. object versions of primitives):
public static class ByteRef extends Ref<Byte> {
public ByteRef() {
super((byte)0);
}
public ByteRef(byte value) {
super(value);
}
// I'm not sure, if I like this one :-)
public void set(int value) {
super.set((byte)value);
}
}
Instead of subclassing, you can also add new methods to Ref class:
public static class Ref<T> {
T value;
public Ref(T initValue) {
this.set(initValue);
}
public void set(T Value) {
this.value = Value;
}
public T get() {
return this.value;
}
public static Ref<Byte> createByteRef() {
return new Ref<Byte>((byte)0);
}
public static Ref<Byte> createByteRef(byte value) {
return new Ref<Byte>(value);
}
}
Or you could create separate factory classes: public class Refs { public static Ref createByteRef() { return new Ref((byte)0); }
public static Ref<Byte> createByteRef(byte value) {
return new Ref<Byte>(value);
}
}
The last option is to use reflection to get the parameterized type. I personally would not use this solution, because number of primitive classes is finite and you have option to create much neater interfaces with subclassing
public abstract static class PrimitiveNumberRef<T extends Number> extends
Ref<T> {
private Class<T> type;
public PrimitiveNumberRef() {
// This requires default constructor for Ref class
type = getGenericType(getClass());
super.set((T) getInitialValue(type));
}
@Override
public void set(T value) {
if (value == null) {
throw new IllegalArgumentException(
"Null value is not allowed for PrimitiveNumerRef type: "
+ type);
}
if (!type.isInstance(value)) {
throw new IllegalArgumentException("Unsupported value type: "
+ value.getClass());
}
super.set(value);
}
@SuppressWarnings("unchecked")
private static <T> Class<T> getGenericType(Class<?> clz) {
return (Class<T>) ((ParameterizedType) clz.getGenericSuperclass())
.getActualTypeArguments()[0];
}
private static <T> T getInitialValue(Class<T> clz) {
if (clz == Byte.class) {
return clz.cast((byte) 0);
} else if (clz == Short.class) {
return clz.cast((short) 0);
} else if (clz == Integer.class) {
return clz.cast((int) 0);
} else if (clz == Double.class) {
return clz.cast((double) 0);
} else if (clz == Float.class) {
return clz.cast((float) 0);
} else if (clz == Long.class) {
return clz.cast((long) 0);
} else {
throw new IllegalArgumentException("Unsupported type: "
+ clz.getName());
}
}
}
PrimitiveNumberRef is instantiated as follows:
Ref<Long> val1 = new PrimitiveNumberRef<Long>() { };
来源:https://stackoverflow.com/questions/35946321/java-primitive-counterpart-of-byte-integer-long-etc-in-template