限制某些接口上的 lambda

Restrict lambdas on certain interfaces

假设我有几个接口只有一个抽象方法。有了这些接口,我可以用它来声明 lambda:

interface A {
    int c();
}

interface B {
    int c();
}

public class Main {
    public static void main(String... args) {
        A a = () -> 42;
        B b = () -> 42;
    }
}

简短的问题:是否有一些技巧或 hack 来限制对 lambdas 使用接口 A 并在尝试这样做时构建失败?欢迎任何暗示,无论是否肮脏('dirty' 我的意思是 compilation/bytecode 级别的黑客攻击 - 这不会影响来源,最好是 public 合同)。

长话短说:对于某些接口实现者,我考虑将 equals/hashCode 定义为契约的一部分。另外,我在构建时自动为他们生成 equals/hashCode

在这种情况下,lambda 是麻烦制造者。对于接口 A 的普通和匿名实现者,我可以找到一个 .class 文件并在构建时检测其字节码。对于 lambda,有 VM-anonymous class,在 运行 时间生成。在构建时影响这样的 class 似乎是不可能的,所以我至少需要为一组特定的接口禁止这种情况。

经过一番尝试,invokedynamic 调用的 desc 字段似乎包含正在实现的接口。例如,当我创建一个简单的 () -> {} Runnable 然后通过 ASM's Bytecode Outline 插件传递它时,"ASM-ified" 调用看起来像:

mv.visitInvokeDynamicInsn("run", "()Ljava/lang/Runnable;", new Handle...

因此,如果您能够在调用站点上执行构建时 hack(而不是以某种方式将注释本身标记为 non-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
        }
    }
}

https://dumpz.org/2708733/

想法是用默认实现覆盖父接口

来自发起人的编辑: 经过一番考虑,我决定接受这个答案(因为它最适合我的需要并且实施起来相当便宜)并添加了一些正式的内容。事实上,已经意识到足以防止接口被用作 lambda 类型的 minimal 检测只是将默认实现添加到其抽象方法。