4

假设有一个Row具有多个类型参数的框架类型,以及一个处理该Row类型实例并使用所有这些类型参数的方法。

我有一种方法可以同时处理任何类型Row,甚至是不同的类型,所以显然我使用的是通配符类型Row<?,?>。问题是,如何调用采用 aRow<R,K>和 a的方法Row<?,?>

我的思路:我不完全知道什么是类型Row<?,?>,但肯定是某种Row好的。而当一个泛型方法采用Row<R,K>时,它意味着它想要做一些事情,RK除此之外它可以处理任何类型的Row. 所以我的“任何”类型应该与采用“任何”类型的方法一起使用,对吧?

我在下面附上了我尝试过的示例代码。最奇怪的是最后一行确实有效,但它并不比我认为的其他任何东西都更安全。所以基本上我想要一个比这更干净的解决方案,或者解释为什么这是要走的路。

package foo;

public class Experiment {
  // Defined by a framework.
  interface Key<K extends Key<K>> {}

  interface Row<R extends Row<R, K>, K extends Key<K>> {}

  static <R extends Row<R, K>, K extends Key<K>> R copyRow(R row) {
    return row;
  }

  // My experiments below.
  static class Wrapper<R extends Row<R, K>, K extends Key<K>> {
    public final R row = null; // fixme
    public final Class<R> clazz = null; // fixme
  }

  static <R extends Row<R, K>, K extends Key<K>> R upcast(Row<?, ?> row) {
    return (R) row;
  }

  static <R extends Row<R, K>, K extends Key<K>> R upcast(Row<?, ?> row, Class<R> clazz) {
    assert row.getClass().equals(clazz);
    return (R) row;
  }

  public static void main(String[] args) {
    Wrapper<?, ?> wr = null; // fixme
    copyRow(wr.row); // Compilation error
    copyRow(upcast(wr.row)); // Compilation error
    copyRow(upcast(wr.row, wr.clazz)); // This works, why?
  }
}

(您可以将此示例直接发送到 javac 以查看会发生什么。使用 Java 1.8:https ://pastebin.com/LB10ySsD )

4

1 回答 1

0

这是一种可能性:

public class WildcardsExperiment {
  // Defined by a framework.  <begin unmodifiable>
  interface Key<K extends Key<K>> {}
  interface Row<R extends Row<R, K>, K extends Key<K>> {}

  static <R extends Row<R, K>, K extends Key<K>> R copyRow(R row) {
    return row;
  }
  // <end unmodifiable>

  interface NaturalRowTransformer {
    <R extends Row<R, K>, K extends Key<K>> R apply(R r);
  }

  class Wrapper<R extends Row<R, K>, K extends Key<K>> {
    private final R row = null; // fixme (constructor etc.)
    public final Class<R> clazz = null; // fixme

    R invokeNat(NaturalRowTransformer nat) {
      return nat.apply(row);
    }
  }

  static final NaturalRowTransformer CopyRow = new NaturalRowTransformer() {
    public <R extends Row<R, K>, K extends Key<K>> R apply(R row) {
      return copyRow(row);
    }
  };

  public static void main(String[] args) {
    Wrapper<?, ?> wr = null; // fixme
    wr.invokeNat(CopyRow); // compiles
  }
}

本质上,该copyRow方法被包装在一个访问者NaturalRowTransformer中,这保证了它可以处理所有可能的 F 有界类型对R和的有效组合K。然后Wrapper提供了一个接受方法invokeNat,它接受访问者并在具体的范围内执行操作copyRow(不是通配符)。RK

该技巧受到类别理论(因此名称中的“自然”)的启发,并从 Scala 导入,尽管当前 Scala 对类型的模式匹配的实现允许更简洁的解决方案。众所周知,这种解决方案可以在稍微复杂的约束条件下工作。

于 2018-05-21T18:39:55.413 回答