如何从通配符类型转换为参数化类型?

时间:2018-05-21 17:48:32

标签: java generics types wildcard f-bounded-polymorphism

我们假设有一个带有多个类型参数的框架类型Row,以及一个与Row类型的实例一起使用并使用所有这些类型参数的方法。

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

我的想法:我不确切地知道Row<?,?>是什么类型,但它肯定是某种Row好的。如果通用方法需要Row<R,K>,则表示它希望对RK执行某些操作,但除此之外,它可以处理任何类型的Row。所以我的&#34;任何&#34;类型应该使用一个方法,该方法需要&#34;任何&#34;类型,对吧?

我将下面的示例代码附加到我尝试过的内容中。最奇怪的是,最后一行实际上是有效的,但它并不比我认为的任何其他类型更安全。所以基本上我想要一个更清洁的解决方案,如果可能的话,或者解释为什么要这样做。

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

1 个答案:

答案 0 :(得分: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-bounded类型对RK的所有可能有效组合。 Wrapper然后提供了一个接受方法invokeNat,它接受​​访问者并在copyRowR具体的范围内执行K操作(不是通配符) )。

这个技巧的灵感来自类别理论(因此名称中的“自然”),并从Scala导入,尽管当前Scala模式匹配的实现允许更简洁的解决方案。已知此解决方案可在slightly more complex constraints下工作。