Java 在某些接口上限制lambda
假设我有几个接口和一个抽象方法。有了这些接口,我可以用它声明lambdas:Java 在某些接口上限制lambda,java,jvm,jvm-bytecode,Java,Jvm,Jvm Bytecode,假设我有几个接口和一个抽象方法。有了这些接口,我可以用它声明lambdas: interface A { int c(); } interface B { int c(); } public class Main { public static void main(String... args) { A a = () -> 42; B b = () -> 42; } } 简短问题:是否存在一些技巧或黑客来限制对la
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
在这种情况下,lambda是麻烦制造者。对于接口
A
的普通和匿名实现者,我可以找到.class
文件,并在构建时插入其字节码。对于lambdas,有一个在运行时生成的VM匿名类。在构建时影响这样的类似乎是不可能的,所以我需要至少禁止特定接口集出现这种情况。从一点做起,它看起来像invokedynamic
调用的desc
字段包含正在实现的接口。例如,当我创建一个简单的()->{}
Runnable并通过插件传递它时,“ASM-ified”调用如下所示:
mv.visitInvokeDynamicInsn(“run”,“()Ljava/lang/Runnable;”,新句柄。。。
因此,如果您能够在调用站点上进行构建时黑客攻击(而不是以某种方式将注释本身标记为非lambda-able,我认为您无法做到这一点)然后,您应该能够首先编译一组不允许的接口,然后对照该集检查
invokedynamic
的描述。请查看我的解决方案:
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类型的最小工具就是将默认实现添加到其抽象方法中。我已经在“长话短说”中描述了所有原因。这还不够详细吗?嗯,lambda不能有
equals
和hashCode
,因为您根本无法定义它们,那么为什么要禁止将此接口用作@功能接口
?@Eugene,想象一下定义equals和hashCode是接口a
服务的合同的一部分。就像这样在它的javadoc-中说,接口A的所有实现者都必须按照这个和那个准则定义equals和hashCode…工具X可以为您生成它们。
。然后有人带着lambdas过来,暴力地破坏了合同,而工具X对此无能为力。当我定义接口A
时,我并不认为它不是函数接口。我希望它只是一个有一个方法的接口。为什么首先要定义hashcode/equals?这样你就可以把这些类型放在一个HashMap
中,但既然lambdas没有这些类型,把它们放在这样一个结构中会导致非常奇怪的行为。我的观点是IMO工具X不应该做任何事情另一方面,在编译时,没有任何@notafunctioninterface
会禁止您使用它。好吧,您甚至无法对此采取任何措施,比如进入LambdaMetafactory
并禁止在同一页面上不将某些接口视为函数-您可以轻松地记录是使用太多,而不是失败。想想键和HashMap
-你可以在将键插入映射后更改它,但行为是未指定的。顺便说一句,如果我是使用你正在做的任何项目的开发人员,我会非常惊讶。。。(我把它作为一个社区wiki发布,因为我根本没有使用过它,我只提供了一个答案的萌芽。)谢谢你的回答,我明白了。这似乎是可行的。注释非lambda接口实际上不是问题。我仍然需要以某种方式确定集合。你不能将hashCode
/equals
方法注入接口,所以你必须将lambda创建站点重定向到另一个能够生成e所需的实现类(或支持从中继承的特定超类)。但这是可行的。您的示例给了我一个很好的提示。理论上,我可以将接口的方法a
检测为默认值,并引发一些运行时异常-这将阻止它成为lambda类型。这将非常好地完成工作。缺点是它对开发人员来说不是那么透明,并且对cod影响很小是的,这只是你的公司/团队采用的方法的问题,也就是说,如果需要运行时异常,那么就可以:)我个人更喜欢编译时检查。当然,我们都喜欢类型安全和尽可能快地失败。但生活是艰难的。我想知道为什么一开始就很难发明某种@NonFunctionalInterface
。问题是lambdareRestrictedInterface
不再是@FunctionalInterface
由于MyInterface
仍然是公共的(而且我们还没有密封的接口),任何人都可以使用它作为lambda。我不知道这是如何解决问题的,但我可能会有一个缓慢的早晨…lambdareRestrictedInterface的目的是代理黑盒MyInterface,并将其替换为需要lamda的客户端代码。另一方面,如果您可以直接访问MyInterface
如果可能的话,您可以删除lambdaridenterface
并修改客户端代码。