如何确保在 Java 8 编译时方法签名 "implements" 是功能接口

How to ensure at Java 8 compile time that a method signature "implements" a functional interface

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

假设我有一个功能接口:

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

和一个包含 3 个静态方法的库 "implementing" 这个功能接口:

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) 具有相同的签名?

一种方法是直接从 LongHashes class:

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

但这会向您的 LongHashes class 添加一些代码并将其绑定到 LongHasher 接口,这可能不是您所希望的(尽管这正是您所要求的)。

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

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 将在运行时挂在内存中,您可以将此声明移动到单独的 class(例如,嵌套),它编译但从不加载。

我过去也希望如此,但不,你不能那样做。但是你知道。 Java 之前有 Java 8. 改为:

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);
    ...
}

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

class LongHashes {

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

    ... etc


    LongHashMap map = createHashMap(LongHashes.randomHash);

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

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

这里把函数写成静态方法:

  • 让人难以理解
  • 听起来像是在重新发明轮子;轮子是 interfaces/classes,通过在问题描述中引号之间使用 "interface" 来暗示重新发明 ;-)

我们没有被迫在所有地方使用 lambda。

虽然我发现 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));
    }
}