【问题标题】:How to ensure at Java 8 compile time that a method signature "implements" a functional interface如何在 Java 8 编译时确保方法签名“实现”功能接口
【发布时间】:2015-05-12 16:35:04
【问题描述】:

在 Java 8 中是否有任何类似 implements 关键字的方法?

假设我有一个功能接口:

@FunctionalInterface
interface LongHasher {
    int hash(long x);
}

还有一个包含 3 个静态方法的库“实现”这个功能接口:

class LongHashes {
    static int xorHash(long x) {
        return (int)(x ^ (x >>> 32));
    }
    static int continuingHash(long x) {
        return (int)(x + (x >>> 32));
    }
    static int randomHash(long x) {
         return xorHash(x * 0x5DEECE66DL + 0xBL);
    }
}

将来我希望能够互换地使用对这 3 种方法的任何引用作为参数。例如:

static LongHashMap createHashMap(LongHasher hasher) { ... }
...
public static void main(String[] args) {
    LongHashMap map = createHashMap(LongHashes::randomHash);
    ...
}

如何在编译时确保LongHashes::xorHashLongHashes::continuingHashLongHashes::randomHashLongHasher.hash(long x) 具有相同的签名?

【问题讨论】:

  • 一个(不相关的)旁白:你的哈希函数看起来像LongToIntFunction。 (我不确定是否应该引入与现有接口“结构上相同”的这种(新)接口 - 但也许给它一个 name 来暗示它会带来什么好处用于...)

标签: java java-8 functional-interface


【解决方案1】:

过去我也希望这样做,但不,你不能那样做。但你知道。在 Java 8 之前就有 Java。改为这样做:

enum LongHashes implements LongHasher {
    XOR {
        @Override
        public int hash(long x) { ... }
    },
    CONTINUING {
        @Override
        public int hash(long x) { ... }
    },
    RANDOM {
        @Override
        public int hash(long x) { ... }
    }
}

然后:

public static void main(String[] args) {
    LongHashMap map = createHashMap(LongHashes.RANDOM);
    ...
}

【讨论】:

  • 这对 8 岁前有好处。在 java 8 中,直接使用 lambda XOR=x->... 更简单
  • @Yahor 如果您的意思是答案中的代码是否可能,绝对是。如果您的意思是XOR=x-> 是否可以在enum 中使用,则否;但是您不需要枚举,只需声明静态字段LongHasher XOR=x->{...} 它看起来就像一个简单的方法。
  • @bayou.io:你可以并不意味着你必须这样做。我只是想指出,开发人员现在不需要将所有东西都硬塞进函数中。
  • @Lukas Eder:我只是想指出之前的想法,因为bayou.io 的评论听起来像是将 lambda 表达式与enum 结合起来是不可能的,我认为,链接可以避免内部长时间的讨论cmets。
【解决方案2】:

没有您要求的这种语法结构。但是,您可以创建一个静态常量,在其中显式地将方法引用分配给您的接口:

class LongHashes {
    private static final LongHasher XOR_HASH = LongHashes::xorHash;
    private static final LongHasher CONTINUING_HASH = LongHashes::continuingHash;
    private static final LongHasher RANDOM_HASH = LongHashes::randomHash;

    static int xorHash(long x) {
        return (int)(x ^ (x >>> 32));
    }
    static int continuingHash(long x) {
        return (int)(x + (x >>> 32));
    }
    static int randomHash(long x) {
         return xorHash(x * 0x5DEECE66DL + 0xBL);
    }
}

这样,如果方法签名或接口以不兼容的方式更改,您的编译将中断。如果你愿意,你可以声明它们public 并使用而不是方法引用。

如果您担心这些静态 lambda 在运行时会挂在内存中,您可以将此声明移动到单独的类(例如,嵌套),该类可以编译但从不加载。

【讨论】:

  • 私有静态方法中的这种方法会更短:LongHasher[] longHashes = {LongHashes::xorHash, LongHashes::randomHash, LongHashes::continuingHash};
【解决方案3】:

您可以声明函数对象,而不是方法。

class LongHashes {

    static final LongHasher xorHash = x -> {
        return (int)(x ^ (x >>> 32));
    };

    ... etc


    LongHashMap map = createHashMap(LongHashes.randomHash);

【讨论】:

    【解决方案4】:

    一种方法是直接从LongHashes 类返回LongHasher

    class LongHashes {
      private static int xorHashImpl(long x) {
        return (int)(x ^ (x >>> 32));
      }
      static LongHasher xorHash() {
        return LongHashes::xorHashImpl;
      }
    }
    

    但这会在您的 LongHashes 类中添加一些代码并将其绑定到 LongHasher 接口,这可能是不可取的(尽管这基本上是您所要求的)。

    【讨论】:

    • 它可以进一步简化,不是吗:) 你不需要xorHashImpl 方法,只需要内联lambda;而且您不需要xorHash 作为方法,只需有一个静态字段即可。
    【解决方案5】:

    或者只创建 3 个实现 LongHasher 的类。当你需要一个 LongHasher 时,获取或创建一个实例并传递它:

    LongHasher longHasher = ... // new RandomLongHasher(), factory, .......
    LongHashMap map = createHashMap(longHasher);
    

    在这里将函数编写为静态方法:

    • 让人难以理解
    • 听起来像是在重新发明轮子;轮子是接口/类,通过在问题描述中的引号之间使用“接口”来暗示重新发明;-)

    我们不必在任何地方都使用 lambda。

    【讨论】:

      【解决方案6】:

      虽然我发现 Tagir 的答案是一个不错的 hack,但在创建新哈希器时很容易忘记添加私有常量。

      像往常一样,在处理潜在的重构问题时,我认为测试就是答案:

      public class LongHashesTest {
      
          @Test
          public void xorHash() {
              LongHasher xorHash = LongHashes::xorHash;
      
              assertEquals(1768181579, xorHash.hash(34312465426524234l));
          }
      
          @Test
          public void continuingHash() {
              LongHasher continuingHash = LongHashes::continuingHash;
      
              assertEquals(1529080340, continuingHash.hash(74543524355l));
          }
      
          @Test
          public void randomHash() {
              LongHasher randomHash = LongHashes::randomHash;
      
              assertEquals(-1100764221, randomHash.hash(4343245345432154353l));
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2012-05-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-08-21
        • 1970-01-01
        相关资源
        最近更新 更多