2016-11-11 4 views
1

So hatte ich ein Programm, das auf Java 7 gut funktionierte, aber es konnte nicht mit einem Fehler in Java 8 kompilieren. Also, ich ging durch Java Kompatibilitätsprobleme und ich denke, ich habe den Grund herausgefunden (Oder habe ich? http://bugs.java.com/bugdatabase/view_bug.do?bug_id=7144506). Ich möchte wissen, ob es eine kurze und effiziente Möglichkeit gibt, zu replizieren, was mein ursprüngliches Code-Snippet mit Java 8 macht. Hier ist ein Programm, das die Situation darstellt. In meinem Code gibt es eine Setter-Methode, die das Array füllt und ich habe keine Kontrolle darüber, wie sie gefüllt wird.Java 8 Kompatibilitätsproblem: Wie Objekt Array zu Subtype-Liste in Java 8 konvertieren?

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

Der Fehler natürlich infrage kommen:

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      

Ich brauche ein (vorzugsweise eine Zeile/elegant) alternative Verfahren, die auch wirksam ist, das gleiche zu erreichen.

EDIT: Um zu klären. Ich kann nicht kontrollieren, was Klasse B mir zurückgibt. Ich kann auch Klasse A nicht ändern (ich glaube nicht, dass das in jedem Fall helfen könnte). Ich kann nur kontrollieren, was HelloWorld macht.

+1

* Ich habe keine Kontrolle darüber, wie es ausgefüllt wird *: Bedeutet das, dass Sie keine Ahnung haben, ob alles, was es enthält, Instanzen von A ist? Was möchten Sie tun, wenn es Strings oder Ganzzahlen anstelle von As enthält? –

+1

Sie haben die erste Frage beantwortet, aber nicht die zweite. Wenn das Array von Objekten etwas anderes als Instanzen von A enthält, was sollte Ihre Methode tun? –

+0

@ jb-nizet Nein, ich weiß sicher, dass es Instanzen von A enthält, nur dass ich es nicht kontrollieren kann. Grüße. –

Antwort

4

Wenn Sie keinen Zugang zu B, aber sind Sie sicher, dass Sie eine der bekommen, könnten Sie so etwas wie dies versuchen:

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()); 

Wie @JBNizet darauf hingewiesen: die wirklich am einfachsten, aber unsicher und daher nicht wirklich empfohlene Lösung ist nur eine List -Cast hinzufügen: nur die A

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

Wenn Sie unsicher sind, über das, was in dem Array ist, sollten Sie eher die folgenden Befehle verwenden, um herauszufiltern:

oder das gleiche mit Verfahren Referenzen:

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()); 

EDIT: hinzugefügt objArraygetA() Aufruf wegzulassen doppelt so Antwort auf @ Holger gesehen. Das zweimalige Anrufen könnte teurer sein als der Rest der vorgeschlagenen Lösungen.

+0

Vielen Dank. Könntest du sagen, ob es ähnlich effizient wie der alte Code wäre, da ich keine Erfahrung mit Stream habe? Grüße. –

+1

Es mag etwas langsamer (Millisekunden? Nanosekunden? 0?) Langsamer sein, aber Sie behalten die volle Kontrolle darüber, wie die Transformation abläuft, wenn aus irgendeinem Grund nicht nur A's drin sind. Wenn die Leistung jedoch ein Problem ist (z. B. wenn Sie große Arrays verwenden), haben Sie die Möglichkeit, parallel zu arbeiten: 'Stream.of (someObject.getA()) .parallel() ...'. – Roland

+2

Wenn Sie den unsicheren Code, den Sie haben, unbedingt behalten wollen, und der brechen könnte, wenn Sie ein Element aus der Liste entfernen, wenn es kein A ist, können Sie einfach 'List verwenden a = someObject.getA() == null? Collections.emptyList(): ((Liste) Arrays.asList (someObject.getA())); '. Ich würde es jedoch nicht empfehlen. –

3

Die sicherste Lösung ist das Array in ein Array von der richtigen Art zu kopieren, dh

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

mit Java 8, gibt es die Möglichkeit, statt die Stream-API zu verwenden, die nur etwas kürzer:

Wenn Sie sich über die Kosten für das Kopieren von Arrays sorgen, haben Sie in Ihrem ursprünglichen Code zweimal getA() aufgerufen, was sogar noch höhere Kosten verursacht als nur eine einfache Array-Kopie. Die obigen Lösungen beheben das.

Unkontrollierte Operationen ausführen, "weil ich weiß, was ich mache" zahlt sich selten aus. In diesem speziellen Fall verschieben Sie einfach die Kosten von der Listenerstellung zu dem Ort, an dem die Liste verwendet wird.

+0

Vielen Dank! Ihre erste Herangehensweise ist das, was ich persönlich zuerst herausgefunden habe. Dann war ich mir nicht sicher, wie sich die Speicherkosten auf die Funktionalität auswirken würden. Aber ich denke du hast Recht. Das ist der richtige Weg. Glauben Sie, dass der Stream-Ansatz langsamer ist als der erste? Wie vergleicht es auch mit dem von Roland vorgeschlagenen Map -> Collect-Ansatz? –

+2

'Stream.toArray' ist in diesem Szenario potentiell effizienter als' Collectors.toList() ', da es die Anzahl der Elemente im Voraus kennt. Es ist also gleichauf mit 'Arrays.copyOf'. – Holger

+1

Ich mag die 'toArray (A [] :: new)' -Variante. Jetzt hängt es hauptsächlich davon ab, ob die Liste "" nachträglich geändert wird oder nicht. – Roland