2016-04-18 5 views
4

私は、コードのこの部分をコンパイルしようとしている:この場合、1引数のインスタンスメソッドのBiConsumerに対する型推論はなぜ異なるのですか?

import java.util.Collection; 
import java.util.function.BiConsumer; 
import de.hybris.platform.servicelayer.exceptions.ModelSavingException; 
import de.hybris.platform.servicelayer.model.ModelService; 

public class Foo { 

    public static interface ModelService2 { 
     public abstract void saveAll(Object[] paramArrayOfObject) throws ModelSavingException; 
     public abstract void saveAll(Collection<? extends Object> paramCollection) throws ModelSavingException; 
     public abstract void saveAll() throws ModelSavingException; 
    } 

    public void bar() { 
     final BiConsumer<ModelService2, Collection<? extends Object>> consumer1 = ModelService2::saveAll; 
     final BiConsumer<ModelService, Collection<? extends Object>> consumer2 = ModelService::saveAll; 
    } 
} 

インターフェースModelServiceは、SAP HYBRISプラットフォームによって定義されます。 ModelService2は、ハイブリースプラットフォームのインターフェイスに定義された名前saveAllでオーバーロードされたメソッドを複製するだけです。

上記をコンパイルするときに、私は次のコンパイラエラーを取得する:インターフェースのそれぞれがどこにあるか、私は見つけることができるよ唯一の違いはあるとき

1. ERROR in src\Foo.java (at line 17) 
    final BiConsumer<ModelService, Collection<? extends Object>> consumer2 = ModelService::saveAll; 
                      ^^^^^^^^^^^^^^^^^^^^^ 
Cannot make a static reference to the non-static method saveAll(Object[]) from the type ModelService 

はなぜコンパイラはModelServiceに異なる型推論をしますか?

この場合、コンパイルにはjavac 1.8.0_77を使用しています。たとえばEclipseでは、上記のコードのエラーは報告されません。

EDIT:

比較的似エラーは、次の変数宣言にも起こる:

final Consumer<ModelService2> consumer3 = ModelService2::saveAll; 
final Consumer<ModelService> consumer4 = ModelService::saveAll; 

この場合、コンパイルエラーがある:

1. ERROR in src\Foo.java (at line 19) 
    final Consumer<ModelService> consumer4 = ModelService::saveAll; 
              ^^^^^^^^^^^^^^^^^^^^^ 
Cannot make a static reference to the non-static method saveAll(Object[]) from the type ModelService 

EDIT2:

コンパイル引数は次のとおりです。

'-noExit' 
'-classpath' 
'<classpath>' 
'-sourcepath' 
'<source path>' 
'-d' 
'<path>\classes' 
'-encoding' 
'UTF8' 

EDIT 3:
これらは、Eclipseのクラスファイルビューアで示される3つのメソッドの定義は以下のとおりです。

// Method descriptor #43 (Ljava/util/Collection;)V 
    // Signature: (Ljava/util/Collection<+Ljava/lang/Object;>;)V 
    public abstract void saveAll(java.util.Collection arg0) throws de.hybris.platform.servicelayer.exceptions.ModelSavingException; 

    // Method descriptor #45 ([Ljava/lang/Object;)V 
    public abstract void saveAll(java.lang.Object... arg0) throws de.hybris.platform.servicelayer.exceptions.ModelSavingException; 

    // Method descriptor #10()V 
    public abstract void saveAll() throws de.hybris.platform.servicelayer.exceptions.ModelSavingException; 

解像度:

この問題は、java v4.4.1のEclipseコンパイラによって発生します。少なくともv4.5.1以降は修正されています。私は最初にコマンドラインからビルドするときにコードをコンパイルするためにhybrisプラットフォームで使用されていたEclipseコンパイラであることに気付かなかった。

+0

'ModelService2'の' saveAll'メソッドは 'static'メソッドですか?任意の方法でModelServiceを汎用ですか?https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html – Marco13

+0

「メソッド参照の種類」を参照してください。あなたが使用できないクラスのコードを見なければ、私はちょうど推測するだろう。 –

+0

@ Marco13投稿したドキュメントから、ある引数を取って '特定の型の任意のオブジェクトのインスタンスメソッドへの参照 'をしたい – SpaceTrucker

答えて

3

メソッドのオーバーロードとvarargsと型推論とのやりとりは、おそらくJava型チェックの中で最も複雑で毛深い部分です。これはバグが定期的に出現する場所であり、異なるコンパイラ間で違いがある場所です。

ModelServiceは可変引数saveAllがあります

私の推測では、次のようです。このため、2つのオブジェクト引数を持つsaveAllは、そのようなオブジェクトへの有効なメソッド呼び出しです。そのメソッドが静的である場合は、ModelServiceと1つのCollectionで呼び出すことが有効なので、メソッド参照式はBiConsumer<ModelService2, Collection<? extends Object>>タイプに対して有効です。

コンパイラのバグのために、コンパイラはそれをメモし、メソッドが静的ではないことに注意して、メソッド参照式がここでは有効でないことを推論します。コンパイルエラーが発生します。

ModelService2.saveAllは、可変長ではなく、ModelServiceと1つのCollectionで呼び出すことはできません。このため、コンパイラはこの可能性を試してもこのバグに陥ることはありません。

Eclipse 4.5.2とjavac 1.8.0_77でこのコードを試してみると、すべてのサンプルが私のためにコンパイルされました。私はなぜあなたが別の結果を得ているのか分かりません。

+1

varargsはこのようなバグを引き起こすかもしれませんが、 'jdk1.8.0_05'、' jdk1.8.0_20'、 'jdk1.8.0_40'、' jdk1.8.0_51'、 'jdk1.8.0_60'、' jdk1 .8.0_65'も 'jdk1.8.0_77'も問題を再現します... – Holger

+1

@Holger:@SpaceTruckerは' ModelService'のクラスファイルに対してコンパイルすることができますか?それは他のコンパイラでコンパイルされている可能性があります。 ? – Lii

+1

OPに従って、ネストされたインタフェースModelService2の配列パラメータをvarargsメソッドに変更することで、問題が具体化されます。ネストされたクラスの場合、同じコンパイラを使用してコンパイルされたと見なすことができます。 – Holger

関連する問題