假设我有几个带有一个抽象方法的接口。拥有这些接口,我可以用它声明lambdas:
interface A {
int c();
}
interface B {
int c();
}
public class Main {
public static void main(String... args) {
A a = () -> 42;
B b = () -> 42;
}
}
简短的问题:使用lambdas的接口A
是否有一些技巧或黑客限制,并在尝试这样做时构建失败?欢迎任何提示,无论是否肮脏,都表示"肮脏",是指汇编/字节码级别的黑客 - 不会影响来源,最好是公共合同)。
长篇小说:对于某些接口实施者,我认为将equals/hashCode
定义为合同的一部分。另外,我会在构建时间自动生成equals/hashCode
。
在这种情况下,Lambdas是麻烦制造者。对于接口A
的普通和匿名实施者,我可以在构建时找到一个.class
文件并仪器的字节码。对于Lambdas,有在运行时生产的VM匿名类。在构建时间上影响这样的类似乎是不可能的,所以我至少需要禁止针对特定界面的这种情况。
请查看我的解决方案:
package com.example.demo;
public class LambdaDemo {
public static void main(String[] args) {
//doesn't compile
//LambdaRestrictedInterface x = () -> {};
LambdaRestrictedInterface y = new Test();
y.print();
}
private static class Test implements LambdaRestrictedInterface {
@Override
public void print() {
System.out.println("print");
}
}
public interface MyInterface {
void print();
}
public interface LambdaRestrictedInterface extends MyInterface {
@Override
default void print() {
//hack prevents lambda instantiating
}
}
}
https://dumpz.org/2708733/
想法是用默认的impl
覆盖父接口从发起人编辑:在考虑之后,我决定接受此答案(因为它适合我的需求最佳,并且实现非常便宜)。实际上,已经意识到最小仪器足以防止将接口用作lambda-type,只是将默认实现添加到其抽象方法中。
从一点点播放中,看起来invokedynamic
调用的desc
字段包含正在实现的接口。例如,当我创建一个简单的() -> {}
运行,然后通过ASM字节码轮廓插件将其传递时," ASM-IFIEF"调用看起来像:
mv.visitInvokeDynamicInsn("run", "()Ljava/lang/Runnable;", new Handle...
因此,如果您能够在呼叫网站上执行构建时间黑客(与以某种方式将注释本身标记为不可用的lambda-able,我认为您不能这样做),那么您应该是能够首先编译一组不允许的接口,然后检查invokedynamic
的desc。