假设我有几个接口只有一个抽象方法。有了这些接口,我可以用它声明lambdas:
interface A {
int c();
}
interface B {
int c();
}
public class Main {
public static void main(String... args) {
A a = () -> 42;
B b = () -> 42;
}
}
简短的问题:是否有一些技巧或黑客限制使用接口A
为lambda,并尝试这样做失败?任何暗示,无论是否肮脏,都是受欢迎的(肮脏的'我的意思是编译/字节码级别的黑客攻击 - 不会影响来源,最好是公共合同)。
长篇小说:对于某些接口实现者,我考虑将equals/hashCode
定义为合同的一部分。另外,我会在构建时自动为它们生成equals/hashCode
。
在这种情况下,lambdas是麻烦制造者。对于接口A
的普通和匿名实现者,我可以找到.class
文件并在构建时检测其字节码。对于lambdas,有一个VM-anonymous类,在运行时生成。在构建时似乎不可能影响这样的类,所以我需要至少禁止这些场合用于特定的接口集。
答案 0 :(得分:2)
从玩一下,看起来desc
调用的invokedynamic
字段包含正在实现的界面。例如,当我创建一个简单的() -> {}
Runnable然后通过ASM's Bytecode Outline插件传递它时," ASM-ified"电话看起来像:
mv.visitInvokeDynamicInsn("run", "()Ljava/lang/Runnable;", new Handle...
因此,如果您能够在呼叫站点上执行构建时黑客攻击(而不是以某种方式将注释本身标记为非lambda-able,我认为您无法做到)那么你应该能够首先编译一组不允许的接口,然后检查invokedynamic
对该集合的描述。
答案 1 :(得分:2)
请看一下我的解决方案:
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
}
}
}
想法是使用默认impl
覆盖父接口从发起人编辑:经过一番考虑后,我决定接受这个答案,(因为它最符合我的需求并且实施起来相当便宜),并附带一些正式的补充。事实上,人们意识到,足以阻止接口被用作lambda类型的 minimal 检测只是将默认实现添加到其抽象方法中。