Java:反射、泛型类型和未检查类型转换



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

原来的'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转换为Property<Object>,而不是泛型类型:

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的类型擦除限制,我可以使用哪些技术来安全解决这些警告?
  • 我是否可以安全地在原始的非类型化方法中抑制未检查的警告?

将字段值转换为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,但似乎你不需要。

你需要提高你的泛型技能。

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

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的超类型。

相关内容

  • 没有找到相关文章

最新更新