Search code examples
javajvmjvm-bytecode

Restrict lambdas on certain interfaces


Assuming I have a couple of interfaces with exactly one abstract method. Having these interfaces, I can declare lambdas with it:

interface A {
    int c();
}

interface B {
    int c();
}

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

Short question: is there some trick or hack to restrict using interface A for lambdas and fail the build on attempt to do so? Any hint, dirty or not, is welcome (by 'dirty' I mean hacks on compilation/bytecode level - something which won't affect sources and, preferably, public contracts).

Long story: for some interfaces implementors I consider defining equals/hashCode as a part of the contract. Also, I generate equals/hashCode automatically for them at build time.

In this context, lambdas are troublemakers. For ordinary and anonymous implementors of interface A I can find a .class file and instrument its bytecode at build time. For lambdas there is VM-anonymous class, produced at run time. Affecting such class seems impossible at build time, so I need to at least prohibit such occasions for a specific set of interfaces.


Solution

  • Please take a look at my solution on that:

    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/

    Idea is to override parent interface with default impl

    Edit from originator: After some consideration, I decided to accept this answer, (since it suited my needs the best and is rather cheap to implement) with some formal additions. In fact, it was realized that the minimal instrumentation which is enough to prevent interface being used as lambda-type is to just add default implementation to its abstract method.