0

In Kathy Sierra's book on SCJP we learn that, When we write

List <A> list = new ArrayList <A> ();

This means that this list will accept elements of type A only, not it's subtype or supertype, only type A. Then I came across this example

import java.util.*;

interface A {
    public void a();
}
class B implements A {
    public void a() { }
    public void b() { }
}

class C extends B {
    public void a() { }
}

class D extends C {

}

public class Generics {
    public static void main() {
        List <B> lst = new ArrayList <B> ();
        lst.add(new B());
        lst.add(new C());
        lst.add(new D());
    }
}

Here a list has been declared with B as it's bound type. But it looks like it's accepting objects of it's subtype also. Why is it so? And if it is possible then is this declaration available in java

List <? extends B> list = new ArrayList <B> ();

Now with this notation we tell compiler that any type which extends B is allowed to enter this list. Please help, I am really confused

4

4 に答える 4

2

There's a distinct between the types permitted in a collection, and the declared type of the collection itself. For

List <A> list = new ArrayList <A> ();

it's declaring a list of elements. Each element is-a A. Each element can be a subclass of A, but the list itself is viewed as a list of As.

Note the distinction. A banana is-a fruit. But a collection of bananas is not a collection of fruit (counterintuitively). Otherwise you could take that collection of bananas, view it as a collection of fruit, and add an apple.

List <? extends B> list = new ArrayList <B> ();

means that the list can be a list of B, or a list of subtype of B. e.g. the below are all valid:

List <? extends B> list = new ArrayList <B> ();
List <? extends B> list = new ArrayList <C> ();
List <? extends B> list = new ArrayList <D> ();
于 2012-09-21T11:56:42.113 に答える
1

List の例でジェネリクスを教える際の問題は、例を理解できてもルールを理解できないことです。

ジェネリックスの出現により、コンパイラは人間の知識で指示されていないため、 aList<A>要素の順序付けられたシーケンスであることを認識していません(長い湾曲した牙を持つ長鼻類である可能性があります) リストが型の要素のみを受け入れることを保証しAます。コンパイラが認識するすべての情報は、次の定義によって提供されます。

interface List<E> {
  public void add(E);
  public E get(int index);
}

このコードは、単一の型を定義していません: 無限の一連の型を定義しています:

interface ListOfStrings {
  public void add(String s);
  public String get(int index);
}

interface ListOfShapes {
  public void add(Shape e);
  public Shape get(int index);
}

パラメータ化された型をインスタンス化する方法によって異なります。Java では、各サブタイプはそのスーパータイプの有効な代替であるため、 aRectangleは a の の正当な引数List<Shape>ですadd(Shape s)

ジェネリック、ついに手に入れた!」と思うときです。次に、IDE を起動して、次のように入力します。

List<Shape> shapes = new LinkedList<Rectangle>();

コンパイラはコンパイルを拒否します。「- ここで何が起こっているのですか? 形状のリストに四角形を追加できますが、四角形のリストは形状のリストではありませんか?」. 問題は、リスト、形状、および長方形の観点から考える人がいますが、コンパイラーはこれらのいずれも認識していません。見える

T reference = <expression returning type S>

だから、「- Stype の有効な代替品はありますTか?」と疑問に思います。. 別の言い方をすれば、Java の規則に従って構築された型階層Tの親ですか? そのため、古い Java ブックを注意深く調べたところ、いいえ、親ではないSことがわかりました。不正なコード。止まる。X<A>X<B>

「-しかし、長方形のリストには間違いなく形状が含まれています!私は、長方形の束をに追加するプログラムをコンパイルしましたList<Shape>!」. Listこれはコンパイラにとっては何の意味もありません。それほどスマートなプログラムではありません。ご覧のとおり、数年前に学習したいくつかのルールしか解釈できず、 aと aの違いさえわかりませんMammothそれはその古い Java の本しか知りませんが、その本の中で、 とがどのよう関連していても、との間にサブタイプ関係はないX<A>X<B>ABと明確に述べられ、指摘されています。「-ジェネリックだけが配列のように実装されていれば...あなた、愚かなJavaの人々...」実際:

String[] strings = new String[10];
Object[] objects = strings;
objects[0] = new Rectangle(); // ArrayStoreException at runtime

多分あなたは今要点を得ました。結局のところ、それらの Java の人々はそれほど愚かではありません... 少なくとも、彼らが型システムのために選んだ凝った規則は目的を果たします。少なくとも、彼らは実際的な理由でそれを行いました。彼らは配列を共変にしましたが、ジェネリックは不変にしました。配列とリストの内容は両方とも変更できる (ミュータブルである) ため、これにより配列 (リストではなく) が上記の問題にさらされます。たとえば、ScalaList[Integer]は、リストを共変だが不変 ( a は aに代入できますが、読み取り専用) にし、配列を可変だが不変 ( anが必要な場所ではList[Number]使用できませんが、変更は可能) にすることで、タイプ セーフを実現します。その内容)。Array[Integer]Array[Number]

最後に、 と の間のサブタイプ関係をジェネリックの世界に移植するAためBに、ワイルドカードを使用できますが?、それはまったく新しい話の問題です。List<Rectangle>古い Java の本では、を拡張しないと書かれていると思いますList<Shape>が、それは の正当な子ですList<? extends Shape>

于 2012-09-21T16:33:35.710 に答える
0

「これは、このリストがタイプ A の要素のみを受け入れ、サブタイプやスーパータイプではなく、タイプ A のみを受け入れることを意味します。次に、この例に出くわしました」

これは間違っています。どの派生型も受け入れます。また、Javaジェネリックは、消去を使用してコンパイラに実装されています。

B を取るメソッド パラメータを持つのと同じで、B、C、D を受け入れる必要があります。

また、実行時に消去のために型チェックがないことに注意してください。

List <B> lst = new ArrayList <B> ();

なる

List <object> lst = new ArrayList <object> ();

これはあなたを助けるかもしれません:

http://www.ibm.com/developerworks/java/library/j-jtp01255/index.html

于 2012-09-21T11:59:02.783 に答える
0

「これは、このリストがタイプ A の要素のみを受け入れ、サブタイプやスーパータイプではなく、タイプ A のみを受け入れることを意味します」 - これは正しくありません。次のようにコードを書き直すことができます。

List <A> list = new ArrayList <A> ();
B b = new B();
B c = new C();
B d = new D();
list.add(b);
list.add(c);
list.add(d);

変数 b、c、および d は、B、C、および D のインスタンスに割り当てると、すべて「B」型になります。

ご覧のとおり、B、C、および D は A を実装しているため、B、C、D のインスタンスを A に割り当てることができます。

于 2012-09-21T12:08:13.177 に答える