2

有时,当对 Object 使用 Java 反射或一些特殊的存储操作时,最终会出现未经检查的警告。我已经习惯了,当我对此无能为力时,我会记录为什么一个呼叫未被检查以及为什么它应该被认为是安全的。

但是,我第一次收到有关未检查呼叫的错误。这个功能:

public <K,V extends SomeClass & SomeOtherClass<K>> void doSomethingWithSomeMap (Map<K,V> map, V data);

我以为这样称呼它:

Map someMap = ...;
SomeClass someData = ...;
doSomethingWithSomeMap(someMap, someData);

会给我一个未经检查的呼叫警告。Jikes 发出警告,但 javac 给了我一个错误:

错误:SomeClass 中的 <K,V>doSomethingWithSomeMap(java.util.Map<K,V>,V) 无法应用于 (java.util.Map,SomeClass)

有什么方法可以强制它编译并发出警告?

谢谢。

4

4 回答 4

1

你让自己陷入了什么巫术:)?从问题和评论中,我假设您确定您拥有扩展抽象类SomeClass并实现接口的对象SomeOtherClass如果是这种情况,我建议在层次结构中使用一个共享这些属性的中间抽象类。

public abstract class Foo<K> extends SomeClass implements SomeOtherClass<K> {
  ...
}

这样,您可以将静态方法签名简化为:

public <K,V extends Foo<K>> void doSomethingWithSomeMap (Map<K,V> map, V data);

如果您不想更改当前的对象层次结构,您可以使用适配器模式的另一个间接级别来欺骗编译器。

“计算机科学中的所有问题都可以通过另一个层次的间接性来解决。” ——大卫·惠勒

public abstract class SomeClass {    
  public abstract void method1();
}

public interface SomeOtherClass<K> {
  void method2(K value);
}

public class MyClass extends SomeClass implements SomeOtherClass<Integer> {
  @Override
  public void method1() {
    System.out.println("MyClass.method1");
  }

  @Override
  public void method2(Integer value) {
    System.out.println("MyClass.method2(" + value + ")");
  }
}

public class Indirection<K> extends SomeClass implements SomeOtherClass<K> {
  private final Object objectValue;

  public Indirection(final Object value) {
    this.objectValue = value;
  }

  @Override
  public void method1() {
    ((SomeClass) objectValue).method1();
  }

  @Override
  public void method2(K value) {
    @SuppressWarnings("unchecked")
    SomeOtherClass<K> delegate = ((SomeOtherClass<K>) objectValue);

    delegate.method2(value);
  }
}

public static void main(String[] args) {
  Map someMap = new HashMap<Integer, MyClass>();
  SomeClass someData = new MyClass();
  Indirection a = new Indirection(someData);
  doSomethingWithSomeMap(someMap, a, 12);
}

public static <K,V extends SomeClass & SomeOtherClass<K>>
void doSomethingWithSomeMap (Map<K,V> map, V data, K value) {
  data.method1();
  data.method2(value);
}

这将打印:

MyClass.method1
MyClass.method2(12)

于 2010-03-27T03:49:15.353 回答
1

您已经知道问题出在someData. 不幸的是,Java 目前不允许您将交集类型SomeClass & SomeOtherClass用作声明变量的类型,也不允许用作 cat 目标。

但是有一种不太完美的方法来做到这一点,即引入一个虚假类型变量X extends SomeClass & SomeOtherClass,然后声明X someData.

这应该在反射性地产生要分配给的值的方法上完成someData。未经检查的演员表(X)将是必要的,这可以证明是安全的。

总的来说,是这样的:

public <X extends SomeClass & SomeOtherClass>
void doReflectionMagic() {
    Map someMap = ...;
    X someData = (X) ...;
    doSomethingWithSomeMap(someMap, someData);
}
于 2012-09-30T20:07:31.937 回答
0

尝试明确指定它:

//Type2 is subclass of (or implements) SomeClass
Map<Type1, Type2> someMap = ...;
SomeClass someData = ...;
<Type1, Type2>doSomethingWithSomeMap(someMap, someData);
于 2010-03-24T21:26:00.750 回答
0

您指定 V 的第二个界限以扩展 SomeOtherClass。但我假设 SomeClass 没有实现 SomeOtherClass (如果它实现了,你就不需要多个边界)。所以 someData 不符合这个界限,因此编译器错误。变量 someData 需要属于一个既扩展 SomeClass 又实现 SomeOtherClass 的类。

于 2010-03-25T09:53:27.403 回答