簡體   English   中英

java中的簡單通用列表

[英]simple generic list in java

為什么java泛型如此棘手? 我以為我終於理解了,但是eclipse在下面的somOtherMethod中使用下面的getOuterList方法給出了一個錯誤。

protected List<?> getOuterList() {
  // blah blah
}

protected List<? extends Object> getOuterList() {
  // blah blah
}

protected void someOtherMethod() {
  ...
  getOuterList().add((MyObject)myObject);  //compile error
  ...
}

更新 :好的 - 所以我現在理解錯誤。 我對List<?>List<? extends SomeObject>缺乏了解List<? extends SomeObject> List<? extends SomeObject>真的意味着。 在前一種情況下,我認為這意味着可以包含任何內容的列表。 在后一種情況下,我假設它是一組擴展SomeObject的對象的列表。 我理解的正確表示只是List<Object>List<SomeObject> (沒有擴展)。 我認為extend幫助我解決了他們沒有解決的問題。 所以這就是我真正的問題所在:

public interface DogKennel {
  public List<Dog> getDogs();
}

public class GreyHoundKennel implements DogKennel {

  protected List<GreyHound> greyHounds;

  public List<GreyHound> getGreyHounds() {
    return this.greyHounds;
  }

  public List<Dog> getDogs() {
    // Is there no way to handle this with generics
    // w/out creating a new List?
    return getGreyHounds(); //compiler error
  }

}

您說該方法返回“某些未知類型的List ”(您無法添加,因為您無法保證您要添加的內容是該類型的子類型)。 你真的想說,“你想要的任何類型的List ”,所以你必須使方法通用:

protected <T> List<T> getOuterList() {
  // blah blah
}

好的,我剛看了你的更新:

這一切都取決於你打算如何處理getDogs()的結果。 如果您不打算能夠將任何項添加到列表中,那么getDogs()應該返回類型List<? extends Dog> List<? extends Dog> ,然后問題將得到解決。

如果你打算能夠添加東西,並且類型為List<Dog>則意味着你可以添加任何種類的Dog ,然后邏輯上這個列表不能和greyHounds ,因為greyHounds有類型List<GreyHound>所以Dog對象不應該進入它。

這意味着您必須創建一個新列表。 請記住,新列表的任何更改都不會反映在原始列表greyHouds

本聲明:

List<?> getOuterList() { }

正在告訴編譯器“我真的不知道我會找回什么樣的列表”。 然后你基本上執行

list<dunno-what-this-is>.add((MyObject)myObject)

它不能將MyObject添加到它不知道它是什么類型的東西的列表中。

本聲明:

protected List<? extends Object> getOuterList() { ... }

告訴編譯器“這是Object的子類型的列表”。 所以再次,當然你不能轉換為“MyObject”然后添加到對象列表。 因為所有編譯器都知道列表可以包含對象。

但是,您可以這樣做:

List<? super MyObject>.getOuterList() { ... }

然后成功添加MyObject。 那是因為現在編譯器知道List是MyObject的列表,或MyObject的任何超類型,因此它肯定可以接受MyObject。

編輯:至於您的DogKennel示例,我想這個代碼片段可以滿足您的需求:

protected List<GreyHound> greyHounds;

// We only want a List of GreyHounds here:
public List<GreyHound> getGreyHounds() {
    return this.greyHounds;
}

// The list returned can be a List of any type of Dog:
public List<? extends Dog> getDogs() {
    return getGreyHounds();
}

您正在嘲笑Java泛型不是類型參數的多態。

通過你的代碼片段,讓我們分開示例:

protected List<GreyHound> greyHounds; // List<GreyHound> is fine

/** This method returns a lovely List of GreyHounds */
public List<GreyHound> getGreyHounds() { 
  return this.greyHounds;
}

/** Here is the problem.  A List<GreyHound> is not a List<Dog> */
public List<Dog> getDogs() {
  return getGreyHounds(); //compiler error
}

所以你的原始評論是正確的。 兩個列表肯定是不同的,它們之間沒有繼承。 所以,我建議您調查這兩個選項:

  1. 嘗試按照您在評論中的建議返回新列表。 例如, return new ArrayList<Dog>(this.greyHounds);

  2. 你真的需要保留一個特定品種的狗的名單嗎? 也許您應該將數據成員定義為您添加特定GreyHounds的List<Dog> 即, protected List<Dog> greyHoundsOnly; 您可以通過對象的外部界面管理狗窩中允許攜帶哪些狗。

除非你有充分的理由保留特定類型的列表,否則我會認真考慮選項2。

編輯:充實我上面建議的選項:

選項1: 返回新列表 優點:簡單,直接,你得到一個打字列表,它消除了一個線程安全問題(不暴露對世界的內部引用)。 缺點:看似性能成本。

// Original code starts here.
public interface DogKennel {
  public List<Dog> getDogs();
}

public class GreyHoundKennel implements DogKennel {

  protected List<GreyHound> greyHounds;

  public List<GreyHound> getGreyHounds() {
    return this.greyHounds;
  }
// Original code ends here

  public List<Dog> getDogs() {
    // This line eliminates the thread safety issue in returning 
    // an internal reference.  It does use additional memory + cost
    // CPU time required to copy the elements.  Unless this list is
    // very large, it will be hard to notice this cost.
    return new ArrayList<Dog>(this.greyHounds);
  }

}

選項2: 使用不同的數據表示 優點:使用多態性更好,返回原始目標的通用列表。 缺點:這是一個略有不同的架構,可能不適合原始任務。

public abstract class DogKennel {
  protected List<Dog> dogs = new ArrayList<Dog>();
}

public class GreyHoundKennel extends DogKennel {

  // Force an interface that only allows what I want to allow
  public void addDog(GreyHound greyHound) { dogs.add(greyHound); }

  public List<Dog> getDogs() {
    // Greatly reduces risk of side-effecting and thread safety issues
    // Plus, you get the generic list that you were hoping for
    return Collections.unmodifiableList(this.dogs);
  }

}

一般類型的? 意思是“某些特定的類型,但我不知道哪個”。 什么用? 本質上是只讀的,因為你知道實際的類型不能寫它。

已經有一個已接受的答案,但請考慮以下代碼修改。

public interface DogKernel {
    public List<? extends Dog> getDogs();
}

public class GreyHoundKennel implements DogKernel {
    protected List<GreyHound> greyHounds;

    public List<GreyHound> getGreyHounds() {
        return this.greyHounds;
    }

    public List<? extends Dog> getDogs() {
        return getGreyHounds(); // no compilation error
    }

    public static void main(String[] args) {
    GreyHoundKennel inst = new GreyHoundKennel();
    List<? extends Dog> dogs = inst.getDogs();
    }
}

Java泛型確實被破壞了,但並沒有破壞。 BTW Scala通過提供差異處理以非常優雅的方式解決了這個問題。

更新----------

請考慮更新的代碼段。

public interface DogKennel<T extends Dog> {
    public List<T> getDogs();
}

public class GreyHoundKennel implements DogKennel<GreyHound> {
    private List<GreyHound> greyHounds;

    public List<GreyHound> getDogs() {
        return greyHounds; // no compilation error
    }

    public static void main(String[] args) {
        GreyHoundKennel inst = new GreyHoundKennel();
        inst.getDogs().add(new GreyHound()); // no compilation error
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM