Java — Primitive Counterpart of Byte, Integer, Long, etc. in template

时光怂恿深爱的人放手 提交于 2019-12-04 20:38:24

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>() { };
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!