代码之家  ›  专栏  ›  技术社区  ›  fejesjoco

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

  •  4
  • fejesjoco  · 技术社区  · 6 年前

    假设有一个框架类型 Row 具有多个类型参数,以及一个用于 键入并使用所有这些类型参数。

    我有一个方法可以与任何类型的 ,即使同时使用不同的类型,所以显然我使用的是通配符类型 Row<?,?> . 问题是,如何调用 Row<R,K> 用一个 行<?,?gt; ?

    我的思路:我不知道是哪种类型 行<?,?gt; 是的,但肯定是某种 好吧。当一个泛型方法 行<R,K> 这意味着它想做点什么 R K 但除此之外,它可以处理任何类型的 . 所以我的“any”类型应该使用一个采用“any”类型的方法,对吗?

    我在下面附上我尝试过的示例代码。最奇怪的是,最后一行确实有效,但它的类型安全性并没有我想象的那么高。所以基本上我想要一个比这个更干净的解决方案,如果可能的话,或者一个解释为什么这是一条路。

    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 回复  |  直到 6 年前
        1
  •  0
  •   Andrey Tyukin    6 年前

    有一种可能性:

    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 接受访客并执行 复制行 在以下范围内的操作: R 是具体的(不是通配符)。

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