Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/375.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 在某些接口上限制lambda_Java_Jvm_Jvm Bytecode - Fatal编程技术网

Java 在某些接口上限制lambda

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

假设我有几个接口和一个抽象方法。有了这些接口,我可以用它声明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


在这种情况下,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
并修改客户端代码。