2017-11-30 11 views
3

私はちょうど1つの抽象メソッドを持つ2つのインターフェイスを持っていると仮定します。これらのインタフェースを持って、私はそれでラムダを宣言することができます。特定のインターフェイスでlambdaを制限する

interface A { 
    int c(); 
} 

interface B { 
    int c(); 
} 

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

短い質問:いくつかのトリックがあるかラムダのためのインタフェースAを使用して制限し、そうしようとする試みでビルドを失敗するハックを?汚いかどうかのヒントは歓迎です(「ダーティ」とは、編集/バイトコードレベルのハックを意味します。ソースや、好ましくは公開契約に影響を与えません)。

ロングストーリー:一部のインターフェースの実装者のために、契約の一部としてequals/hashCodeを定義することを検討します。また、私はequals/hashCodeをビルド時に自動的に生成します。

この文脈では、ラムダはトラブルメーカーです。インターフェイスAの普通の、そして匿名の実装者のために、私は.classファイルを見つけ出し、ビルド時にそのバイトコードを計測することができます。ラムダの場合、実行時に生成されるVM匿名クラスがあります。そのようなクラスに影響を与えるのはビルド時には不可能と思われるので、少なくとも特定のインターフェイスセットに対してこのような機会を禁止する必要があります。

+1

「Long story」ですべての理由を既に説明しました。それは十分詳細ではありませんか? – skapral

+0

うまくいけば、lambdaは 'equals'と' hashCode'を全く定義できないので、なぜこのインターフェースを '@ FunctionalInterface'として使用できないのでしょうか? – Eugene

+0

@Eugene、equalsとhashCodeが定義されていることが、インタフェース 'A'によって提供されるコントラクトの一部であると想像してください。そのjavadocで言うように - 'インターフェースAのすべての実装者は、これとそのガイドラインに従ってequalsとhashCodeを定義しなければなりません...ツールXがあなたのために生成することができます。そして、誰かがラムダと会い、契約を激しく破り、ツールXはそれについて何もできません。私がインターフェース 'A'を定義したとき、私はそれが機能的なインターフェースであるとは思わなかった。私はそれを1つのメソッドとの単なるインターフェースにしたかったのです。 – skapral

答えて

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 
     } 
    } 
} 

https://dumpz.org/2708733/

アイデアは、発信元から

編集IMPLデフォルトで親インターフェイスをオーバーライドすることである。いくつかの考慮後、私はこの答えを受け入れることに決めました(私のニーズに最も適していて実装にはかなり安いので)。正式な追加があります。実際には、の最小値のインスツルメンテーションは、インタフェースがラムダ型として使用されないようにするために、抽象メソッドにデフォルトの実装を追加するだけで実現されています。

+0

あなたの例は私に良いヒントを与えました。理論的には、私は、インターフェイス 'A'のメソッドをデフォルトに設定し、実行時例外を投げてラムダ型にするのを防ぐだけです。それはかなりうまくいくでしょう。欠点は、開発者にとってはあまり透明ではなく、インターフェイスのコードに多少の影響を与えますが、私はそれを生かすことができると思います。 – skapral

+0

ええ、それはあなたの会社/チームに適用されたアプローチの問題です。つまり、ランタイム例外が望まれるなら、それは大丈夫です。私は個人的にコンパイル時間チェックを好みます。 –

+0

もちろん、タイプセーフティとフェイルas-it-possible可能なアプローチのような私たちはすべて。しかし人生は厳しいです。私は何故最初に何らかの '@ NonFunctionalInterface'を作り出すことがとても難しかったのだろうかと思います。 – skapral

2

少しでも遊んでから、invokedynamicコールのdescフィールドに実装されているインターフェイスが含まれているように見えます。あなたはビルド時のハックを行うことができるしているのであれば

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

:たとえば、ときに私はRunnableをシンプル() -> {}を作成し、ASM's Bytecode Outlineプラグインを通してそれを通過し、「ASM-ified」コールは、ように見えましたコールサイトでは(ノンラムダ可能なアノテーション自体を何とかマークするのではなく、あなたができるとは思っていません)、最初に許可されていないインターフェイスのセットをコンパイルしてから、invokedynamic 'そのセットに対するdesc。

+0

(私はこれをコミュニティウィキとして公開しています。これは実際にはこれで作業していないので、私は答えの雑菌しか提供していません)。 – yshavit

+0

答えをいただきありがとうございます。それは実行可能なようです。非ラムダインタフェースに注釈を付けることは、実際問題ではありません。私はまだ何とかセットを決める必要があります。 – skapral

+0

インターフェイスに 'hashCode' /' equals'メソッドを挿入することはできません。したがって、ラムダ作成サイトを目的の実装クラスを生成する(または継承する特定のスーパークラスをサポートする)別のブートストラップメソッドにリダイレクトする必要があります。しかし、それは実行可能です。 – Holger

関連する問題