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

给你一囗甜甜゛ 提交于 2019-12-03 17:20:30

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!