Java - Generics vs Casting Objects

前端 未结 4 1134
野的像风
野的像风 2021-01-03 10:32

I have a class Data

with a generic attribute

private T value;

is there nicer way to do the following?
ie

相关标签:
4条回答
  • 2021-01-03 11:11

    You could ask if "value" is assignable to the expected class.

    private T value;
    .
    .
    .
    public Object getValueAsObjectOfClass(Class<?> expectedClass) {
        if(!expectedClass.isAssignableFrom(value.getClass())) {
            // abort gracefully
        }
        return expectedClass.cast(value);
    }
    
    0 讨论(0)
  • 2021-01-03 11:19

    The point of generics is NOT to allow a class to use different types at the same time.

    Generics allow you to define/restrict the type used by an instance of an object.

    The idea behind generics is to eliminate the need to cast.

    Using generics with your class should result in something like this:

    Data<String> stringData = new Data<String>();
    String someString = stringData.getValue();
    
    Data<Long> longData = new Data<Long>();
    Long someLong = longData.getValue();
    
    Data<List<String>> listData = new Data<List<String>>();
    List<String> someList = listData.getValue();
    

    You should either use Objects and casting --OR-- use generics to avoid casting.

    You seem to believe that generics allow for heterogeneous typing within the same instance.

    That is not correct.

    If you want a list to contain a mixed bag of types, then generics are not appropriate.


    Also...

    To create a long from a double, use Double.longValue().

    To create a float from a double, use Double.floatValue().

    I recommend reading the documentation.

    0 讨论(0)
  • 2021-01-03 11:33

    The design looks suspicious to me, but to answer your actual question:

    The case for Long-values looks wrong. Your snippet contains a c&p error

    public Long getLongValue() {
        if (value.getClass() != Double.class) // <<- should be Long.class
            throw new Exception("Wrong value type '%s'", value);
        return (Long) value;
        //ugly
    }
    

    thus it should read:

    public Long getLongValue() {
        if (value.getClass() != Long.class)
            throw new Exception("Wrong value type '%s'", value);
        return (Long) value;
        //ugly
    }
    

    However, in order to reduce code duplication, you could introduce a generic helper method

    private T getValue() {
        return value;
    }
    
    private <V> V castValue(Class<V> type) {
      if (!type.isInstance(value)) {
        // exception handling
      }
      return type.cast(value);
    }
    
    public List<String> getValues() {
        return castValue(ArrayList.class);
    }
    
    public String getStringValue() {
        return castValue(String.class);
    }
    

    If you decide to go for that approach, I'd recommend to de-generify the data class since it's irritating to have a type parameter if there is actually no constraint on the instance itself. I'd use Object instead for the field type:

    private Object getValue() {
        return value;
    }
    
    private <V> V castValue(Class<V> type) {
      if (!type.isInstance(value)) {
        // exception handling
      }
      return type.cast(value);
    }
    
    public List<String> getValues() {
        return castValue(ArrayList.class);
    }
    
    public String getStringValue() {
        return castValue(String.class);
    }
    // .. more cases ..
    
    0 讨论(0)
  • 2021-01-03 11:35

    You could just use the type T directly for a simple getter and Class.cast -method for other types:

    public class GenericDataTest
    {
        private static class DataTest<T>
        {
            private T value;
    
            public DataTest(T value)
            {
                this.value = value;
            }
    
            public T getValue()
            {
                return value;
            }
    
            public Object getValueAsType(Class<?> type)
            {
                return type.cast(value);
            }
        }
    
        @Test
        public void testGeneric()
        {
            DataTest<String> stringTest = new DataTest<String>("Test");
            Assert.assertEquals("Test", stringTest.getValue());
            Assert.assertEquals("Test", stringTest.getValueAsType(String.class));
    
            DataTest<Double> doubleTest = new DataTest<Double>(1.0);
            Assert.assertEquals(1.0, doubleTest.getValue());
            Assert.assertEquals(1.0, doubleTest.getValueAsType(Double.class));
        }
    
        @Test(expected = ClassCastException.class)
        public void testClassCastFailure()
        {
            DataTest<String> stringTest = new DataTest<String>("Test");
            Assert.assertEquals("Test", stringTest.getValueAsType(Float.class));
        }
    }
    
    0 讨论(0)
提交回复
热议问题