2016-11-11 9 views
1

Java 7でうまく動作するプログラムがありましたが、Java 8でエラーが発生してコンパイルできませんでした。そのため、Javaの互換性に関する問題が発生し、その理由がわかりましたか?(http://bugs.java.com/bugdatabase/view_bug.do?bug_id=7144506)元のコードスニペットがJava 8で行うことを効率的に複製する方法があるかどうかを知りたいのですが、ここでは状況を表すプログラムがあります。私のコードでは、配列を取り込むという仕事をするセッターメソッドがあり、それがどのように埋め込まれているかを制御することはできません。Java 8の互換性の問題:オブジェクト配列をJava 8のサブタイプリストに変換するにはどうすればよいですか?

import java.util.List; 
import java.util.Arrays; 

public class HelloWorld{ 
    public static void main(String []args){ 
     B someObject=new B(); 
     List<A> a = someObject.getA() == null ? Collections.EMPTY_LIST : Arrays.asList(someObject.getA()); 
     for (A item : a) { 
      System.out.println(item.value); 
     } 
    } 
} 

class A{ 
    String value; 
    public A(String value){ 
     this.value = value; 
    } 
} 

class B{ 
    Object[] getA(){ 
     Object arr[]= new Object[4]; 
     arr[0]=new A("hello"); 
     arr[1]=new A("mello"); 
     arr[2]=new A("jello"); 
     arr[3]=new A("cello"); 
     return arr; 
    } 
} 

コースビーイングの誤差:

HelloWorld.java:8: error: incompatible types: bad type in conditional expression                         
     List<A> a = someObject.getA() == null ? Collections.EMPTY_LIST : Arrays.asList(someObject.getA());                  
                        ^                      
    inference variable T has incompatible bounds                                 
     equality constraints: A                                     
     lower bounds: Object                                      
    where T is a type-variable:                                     
    T extends Object declared in method <T>asList(T...)                               
Note: HelloWorld.java uses unchecked or unsafe operations.                              
Note: Recompile with -Xlint:unchecked for details.                                
1 error      

Iは、(できれ一行/エレガント)も同じことを達成するのに効率的である別の方法を必要とします。

編集:明確にする。私はクラスBが私に返すものを制御することはできません。どのような方法でもクラスAを変更することはできません(私はそれがどんな場合にも役立つとは思わない)。私はHelloWorldだけを制御できます。本当に一番簡単なのが、危険なので、ない:@JBNizetが指摘したように

Object[] objArray = someObject.getA(); // omit calling getA() twice 
List<A> a = objArray == null ? Collections.emptyList() : 
       Stream.of(objArray) 
         .map(o -> (A) o) // or place your transform function in here 
         .collect(Collectors.toList()); 

:あなたはBへのアクセス何を持っていませんが、あなたがAさんを取得することが確実な場合

+1

*どのように入力されているのか制御できません*:それはすべてのインスタンスにAのインスタンスが含まれているかどうか分かりません。 Asの代わりにStrings、またはIntegersが含まれていると、何が起こりますか? –

+1

あなたは最初の質問に答えましたが、2番目の質問には答えませんでした。オブジェクトの配列にAの他のインスタンスが含まれている場合、あなたのメソッドは何をしますか? –

+0

@ jb-nizetいいえ私はそれが私がそれを制御することができないだけのインスタンスを含んでいることを確かに知っています。よろしく。 –

答えて

4

することは、あなたはこのような何かを試みることができます

:あなたは配列であるかについて、不明な点がある場合は、むしろのみAさんをフィルタリングするために、次を使用する必要があります

List<A> a = objArray == null ? Collections.emptyList() : (List)Arrays.asList(objArray); 

:推奨ソリューションは、ちょうどList -castを追加することです

List<A> a = objArray == null ? Collections.emptyList() : 
       Stream.of(objArray) 
         .filter(o -> o instanceof A) 
         .map(o -> (A) o) // or place your transform function in here 
         .collect(Collectors.toList()); 

またはメソッド参照と同じ:

List<A> a = objArray == null ? Collections.emptyList() : 
       Stream.of(objArray) 
         .filter(A.class::isInstance) 
         .map(A.class::cast) // or place your transform function in here 
         .collect(Collectors.toList()); 

編集:@ホルガーの答えに見られる二回としてgetA()を呼び出す省略するobjArrayを追加しました。これを2回呼び出すことは、提案されている他のすべてのソリューションよりも高価になる可能性があります。

+0

ありがとうございます。ストリームでの経験がないので、古いコードと同様に効率的であるかどうかをコメントしてください。よろしく。 –

+1

少し遅いかもしれませんが、何らかの理由でAだけが存在しない場合は、変換がどのように起こるかを完全に制御できます。しかし、パフォーマンスが問題であれば(例えば、大規模な配列を持っている場合)、パラレル化できる可能性があります: 'Stream.of(someObject.getA())。parallel()...'。 – Roland

+2

あなたが絶対に安全でないコードを保持したい場合、A以外の要素をリストから取り除いたときに破損する可能性がある場合は、 'List a = someObject.getA()== null? Collections.emptyList():((List)Arrays.asList(someObject.getA())); '。しかし、私はそれをrecommentしないだろう。 –

3

最も安全な解決策は、すなわち

Object[] array = someObject.getA(); 
List<A> a = array==null? Collections.emptyList(): 
    Arrays.asList(Arrays.copyOf(array, array.length, A[].class)); 

のJava 8と、わずかに短くなっており、代わりにストリームAPIを使用するオプションがあり、正しい型の配列に配列をコピーすることです。

Object[] array = someObject.getA(); 
List<A> a = array==null? Collections.emptyList(): 
    Arrays.asList(Arrays.stream(array).toArray(A[]::new)); 

アレイのコピーコストを心配するなら、元のコードではgetA()を2回呼び出して、単純な配列コピーよりもさらに高いコストを課しています。上記の解決策はそれを修正します。

「私がやっていることを知っているので」チェックされていない操作を実行することはほとんどありません。この特定のケースでは、コストをリスト作成からリストが使用される場所に移動するだけです。

+0

ありがとう!あなたの最初のアプローチは私が個人的に最初に思いついたものです。その後、メモリコストがどのように機能に影響を与えるのかがわかりませんでした。しかし、私はあなたが正しいと思います。これは正しい方法です。 ストリームのアプローチが最初のものより遅いと思いますか?また、Rolandが提案したマップ - 収集手法とはどのように比較されますか? –

+2

'Stream.toArray'は、事前に要素の数を知っているので、このシナリオでは' Collectors.toList() 'よりも効率的です。したがって、 'Arrays.copyOf'と同じです。 – Holger

+1

私は本当に 'toArray(A []​​ :: new)' - バリアントが好きです。今度は主に 'List 'が変更されたかどうかによって決まります。 – Roland

関連する問題