Java:反射,通用类型和未经检查的转换

时间:2015-09-30 03:38:37

标签: java generics reflection

我的课将获得一个Object类。然后我使用反射来迭代该类的声明字段,并在每个字段上使用ChangeListener基类注册Property

原创' createChangeListener'方法看起来像这样:

private void createChangeListener(Property property) {
    property.addListener(new ChangeListener() {
        @Override
        public void changed(ObservableValue observable, Object oldValue, Object newValue) {                        
            Foo.this.propertyChanged(observable);
        }
    });
}

然而,这产生了一个不必要的警告:

warning: [unchecked] unchecked call to addListener(ChangeListener<? super T>) as a member of the raw type ObservableValue
    property.addListener(new ChangeListener() {
        where T is a type-variable:
    T extends Object declared in interface ObservableValue

不要被劝阻,我为Property参数和ChangeListener提供了通用类型:

private void createChangeListener(Property<Object> property) {
    property.addListener(new ChangeListener<Object>() {
        @Override
        public void changed(ObservableValue observable, Object oldValue, Object newValue) {                        
            Foo.this.propertyChanged(observable);
        }
    });
}

...只是现在才被告知我已经将我的问题转移到了反射的来源。下面的代码现在已修改为从原始Property<Object>转换为Property w / o泛型类型:

if (Property.class.isAssignableFrom(field.getType())) {
    createChangeListener((Property<Object>)(field.get(model)));
}

这个以前没有警告的代码现在正在产生头部倾斜:

warning: [unchecked] unchecked cast
    createChangeListener((Property<Object>)(field.get(model)));
required: Property<Object>
found:    Object

问题:

  • ಠ_ಠ
  • 鉴于Java的类型擦除限制,我可以使用哪些技术来安全地解决这些警告?
  • 我可以安全地在原始的非类型化方法中禁止未经检查的警告吗?

2 个答案:

答案 0 :(得分:2)

将您的字段值转换为Property<Object>,明确指出Property泛型参数为Object,这可能是错误的,编译器无法帮助您。例如,该字段的实际类型为Property<String>,您可以执行以下操作:

Property<Object> p = (Property<Object>)(field.get(model)); // compiler warning, runtime ok
p.setValue(new Object()); // runtime ok, but now you're probably stored incompatible value

// somewhere much later in completely different piece of code
String value = this.propertyField.getValue(); // sudden ClassCastException

因此,编译器警告您,转换为Property<Object>您正在假设编译器无法证明,并且您可能实际上有不同的类型,这可能导致将对象置于不正确的状态,这可能会在稍后的某个地方破坏您的程序。

在您的特定情况下,您不想说“我知道通用参数类型是Object”。你想说的是“我不关心通用参数类型”。对于这种情况,有一个特殊的构造<?>。你可以在这里使用它:

void createChangeListener(Property<?> property) {
    property.addListener(new ChangeListener<Object>() {
        @Override
        public void changed(ObservableValue<?> observable, 
                            Object oldValue, Object newValue) {                        
            Foo.this.propertyChanged(observable);
        }
    });
}

你可以安全地施放到Property<?>而不会发出任何警告。但是现在你根本不能打电话给setValue,但似乎你根本不需要。

答案 1 :(得分:1)

你需要提高你的仿制技能。

不要试图通过强制转换来解决它,而是尝试通过自己编写泛型代码来解决它:

private <T> void createChangeListener(Property<T> property) {
  property.addListener(new ChangeListener<T>() {
    @Override
    public void changed(ObservableValue<? extends T> observable, T oldValue, T newValue) {                        
        Foo.this.propertyChanged(observable);
    }
  });
}

此处,编译器可以帮助您进行类型检查。它知道oldValue和newValue将具有某种类型T,并且可以检查您是否做出了不正确的假设。

既然addListener(ChangeListener<? super T>)也会接受超类型<? super T>!)的监听器,以下情况也应该没问题:

private void createChangeListener(Property<?> property) {
  property.addListener(new ChangeListener<Object>() {
    @Override
    public void changed(ObservableValue<?> observable, Object oldValue, Object newValue) {                        
        Foo.this.propertyChanged(observable);
    }
  });
}

编译器应该能够验证此代码是类型安全

确保您了解<?><Object><T><? super T><? extends T>之间的差异,并尝试了解如何在您自己中使用它们码。总是更喜欢更广泛的版本(extendssuper可以产生重大影响!查看Java集合中的示例。)

在上面的示例中,super表示您可以将Object侦听器附加到Property<String>,因为它是String的超类型。