问题
Is there in Java 8 any analogue for implements
keyword for methods?
Let's say I have a functional interface:
@FunctionalInterface
interface LongHasher {
int hash(long x);
}
And a library of 3 static methods "implementing" this functional interface:
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);
}
}
In the future I want to be able to interchangeably use any of references to these 3 methods as a parameter. For example:
static LongHashMap createHashMap(LongHasher hasher) { ... }
...
public static void main(String[] args) {
LongHashMap map = createHashMap(LongHashes::randomHash);
...
}
How can I ensure at compile time that LongHashes::xorHash
, LongHashes::continuingHash
and LongHashes::randomHash
have the same signature as LongHasher.hash(long x)
?
回答1:
There's no such syntax construction you're asking for. However you can create a static constant where you explicitly assign the method reference to your interface:
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);
}
}
This way your compilation will break if either method signature or interface changes in incompatible way. If you want you may declare them public
and use instead of method references.
If you care that these static lambdas will be hanging in memory at runtime, you can move this declaration to the separate class (for example, nested), which compiles but never loaded.
回答2:
I had wished for this too, in the past, but no you can't do that. But you know. There was Java before Java 8. Do this instead:
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) { ... }
}
}
And then:
public static void main(String[] args) {
LongHashMap map = createHashMap(LongHashes.RANDOM);
...
}
回答3:
You could declare function objects, instead of methods.
class LongHashes {
static final LongHasher xorHash = x -> {
return (int)(x ^ (x >>> 32));
};
... etc
LongHashMap map = createHashMap(LongHashes.randomHash);
回答4:
One way would be to return a LongHasher
directly from the LongHashes
class:
class LongHashes {
private static int xorHashImpl(long x) {
return (int)(x ^ (x >>> 32));
}
static LongHasher xorHash() {
return LongHashes::xorHashImpl;
}
}
but that adds some code to your LongHashes
class and ties it to the LongHasher
interface, which may not be desirable (although that's essentially what you are asking for).
回答5:
Or just create 3 classes which implement LongHasher. When you need a LongHasher, get or create an instance and pass it:
LongHasher longHasher = ... // new RandomLongHasher(), factory, .......
LongHashMap map = createHashMap(longHasher);
Writing functions as static methods here:
- makes it difficult to understand
- sounds like reinventing the wheel; the wheel being interfaces/classes, reinventing being hinted by the use of "interface" between quotes in the problem description ;-)
We're not forced to use lambdas everywhere.
回答6:
Although I find Tagir's answer a nice hack, it's easy to forget adding the private constant when one creates a new hasher.
As usual, when dealing with potential refactoring problems, I think that testing is the answer:
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));
}
}
来源:https://stackoverflow.com/questions/30196716/how-to-ensure-at-java-8-compile-time-that-a-method-signature-implements-a-func