意味 - java ジェネリクス メソッド 戻り値



Javaでジェネリックアレイを作成するには? (20)

Class引数をコンストラクタに渡す必要はありません。 これを試して。

static class GenSet<T> {
    private final T[] array;
    @SuppressWarnings("unchecked")
    public GenSet(int capacity, T... dummy) {
        if (dummy.length > 0)
            throw new IllegalArgumentException(
              "Do not provide values for dummy argument.");
        Class<?> c = dummy.getClass().getComponentType();
        array = (T[])Array.newInstance(c, capacity);
    }
    @Override
    public String toString() {
        return "GenSet of " + array.getClass().getComponentType().getName()
            + "[" + array.length + "]";
    }
}

そして

GenSet<Integer> intSet = new GenSet<>(3);
System.out.println(intSet);
System.out.println(new GenSet<String>(2));

結果:

GenSet of java.lang.Integer[3]
GenSet of java.lang.String[2]

Javaジェネリックの実装のために、あなたは次のようなコードを持つことができません:

public class GenSet<E> {
    private E a[];

    public GenSet() {
        a = new E[INITIAL_ARRAY_LENGTH]; // error: generic array creation
    }
}

タイプセーフティを維持しながらこれを実装するにはどうすればよいですか?

私はこのようなJavaのフォーラムで解決策を見た:

import java.lang.reflect.Array;

class Stack<T> {
    public Stack(Class<T> clazz, int capacity) {
        array = (T[])Array.newInstance(clazz, capacity);
    }

    private final T[] array;
}

しかし、私は実際に何が起こっているのか分からない。


Java 8では、ラムダまたはメソッド参照を使用して、一種の汎用配列作成を行うことができます。 これは反射的アプローチ( Classを渡す)と似ていますが、ここではリフレクションを使用していません。

@FunctionalInterface
interface ArraySupplier<E> {
    E[] get(int length);
}

class GenericSet<E> {
    private final ArraySupplier<E> supplier;
    private E[] array;

    GenericSet(ArraySupplier<E> supplier) {
        this.supplier = supplier;
        this.array    = supplier.get(10);
    }

    public static void main(String[] args) {
        GenericSet<String> ofString =
            new GenericSet<>(String[]::new);
        GenericSet<Double> ofDouble =
            new GenericSet<>(Double[]::new);
    }
}

たとえば、これは<A> A[] Stream.toArray(IntFunction<A[]>)によって使用されます。

これは、匿名クラスを使用してJava 8より前でも実行できますが、もっと面倒です。


Object配列を作成し、それをどこにでもEにキャストすることができます。 うん、それはそれを行うには非常にクリーンな方法ではないが、少なくとも動作する必要があります。


あなたが投稿した例で何が起こっているのかという質問に誰も答えなかった。

import java.lang.reflect.Array;

class Stack<T> {
    public Stack(Class<T> clazz, int capacity) {
        array = (T[])Array.newInstance(clazz, capacity);
    }

    private final T[] array;
}

他の人が言っているように、ジェネリック医薬品は編集中に「消去」されます。 したがって、実行時にジェネリックのインスタンスはそのコンポーネントタイプが何であるかを知らない。 この理由は歴史的なもので、Sunは既存のインターフェース(ソースとバイナリの両方)を壊さずにジェネリックを追加したかったからです。

一方、配列は実行時にそのコンポーネントの型を知っています。

この例では、コンストラクタ(型を知っている)を呼び出すコードに、クラスに必要な型を伝えるパラメータを渡すことによって問題を回避します。

したがって、アプリケーションは、クラスを次のようなもので構築します。

Stack<foo> = new Stack<foo>(foo.class,50)

コンストラクタはコンポーネントタイプが何であるかを(実行時に)知るようになり、リフレクションAPIを介して配列を構築するためにその情報を使用することができます。

Array.newInstance(clazz, capacity);

最後に、コンパイラーはArray#newInstance()によって返されたArray#newInstance()が正しい型(わかっていてもArray#newInstance()であることを知る方法がないので、型キャストがあります。

このスタイルは少し醜いですが、何らかの理由(配列の作成、コンポーネントタイプのインスタンスの作成など)のために、実行時にコンポーネントタイプを知る必要があるジェネリックタイプを作成する上で、最も悪い解決策ではないことがあります。


あなたはいつでもこれを行うことができます:

E[] arr = (E[])new Object[INITIAL_ARRAY_LENGTH];

これは、 実効Javaでジェネリックコレクションを実装するために推奨される方法の1つです。 項目26 。 型エラーはなく、配列を繰り返しキャストする必要はありません。 しかし、これは潜在的に危険であり、慎重に使用する必要があるため警告が表示されます。 コメントで詳述されているように、このObject[]E[]型として偽装されており、予期せぬエラーや安全でない場合はClassCastExceptionが発生する可能性がありClassCastException

経験則として、この動作は、キャスト配列が内部的に(たとえばデータ構造を後退させるために)使用され、クライアントコードに返されたり公開されたりしない限り、安全です。 ジェネリック型の配列を他のコードに戻す必要がある場合は、言及したArrayクラスが適切な方法です。

可能であれば、ジェネリックを使用している場合は、配列ではなくList作業するほうがずっと楽しいでしょう。 確かに選択肢がない場合もありますが、コレクションフレームワークの使用ははるかに堅牢です。


あなたはキャストを使うことができます:

public class GenSet<Item> {
    private Item[] a;

    public GenSet(int s) {
        a = (Item[]) new Object[s];
    }
}

このコードスニペットを使って、単純な自動テストユーティリティのために渡されるクラスを具体的にインスタンス化しました。

Object attributeValue = null;
try {
    if(clazz.isArray()){
        Class<?> arrayType = clazz.getComponentType();
        attributeValue = Array.newInstance(arrayType, 0);
    }
    else if(!clazz.isInterface()){
        attributeValue = BeanUtils.instantiateClass(clazz);
    }
} catch (Exception e) {
    logger.debug("Cannot instanciate \"{}\"", new Object[]{clazz});
}

このセグメントに注意してください:

    if(clazz.isArray()){
        Class<?> arrayType = clazz.getComponentType();
        attributeValue = Array.newInstance(arrayType, 0);
    }

配列の開始のためにArray.newInstance(配列のクラス、配列のサイズ) 。 クラスはプリミティブ(int.class)とオブジェクト(Integer.class)の両方になります。

BeanUtilsはSpringの一部です。


この例では、Javaリフレクションを使用して配列を作成しています。 これを行うには、型保証されていないので、一般的にはお勧めしません。 代わりに、内部リストを使用して配列をまったく使用しないでください。


これについては、第5章(Generics)のEffective Java、2nd Edition 、item 25 ...で説明しています。

あなたのコードは動作しますが、チェックされていない警告が生成されます(次の注釈でこれを抑制することができます:

@SuppressWarnings({"unchecked"})

ただし、配列の代わりにリストを使用する方がよいでしょう。

OpenJDKプロジェクトサイトのこのバグ/機能に関する興味深い議論があります


これはタイプセーフな唯一の答えです

E[] a;

a = newArray(size);

@SafeVarargs
static <E> E[] newArray(int length, E... array)
{
    return Arrays.copyOf(array, length);
}

こんにちはスレッドが死んでいますが、私はあなたの注意を引くことを望みます:

Genericsは、コンパイル時の型チェックに使用されます。

  • したがって、目的は何が必要なのかをチェックすることです。
  • あなたが返すのは、消費者が必要とするものです。
  • これをチェックして:

ジェネリッククラスを書くときに、型キャストの警告を心配しないでください。 あなたがそれを使用しているときに心配。


それでもやっかいな簡単な回避策は、メインクラスの内部に2番目の "ホルダー"クラスをネストし、それを使ってデータを保持することです。

public class Whatever<Thing>{
    private class Holder<OtherThing>{
        OtherThing thing;
    }
    public Holder<Thing>[] arrayOfHolders = new Holder<Thing>[10]
}

一般的な配列の作成はjavaでは禁止されていますが、好きなようにすることができます

class Stack<T> {
private final T[] array;
public Stack(int capacity) {
    array = (T[]) new Object[capacity];
 }
}

他の人によって提案された強制的なキャストは、私のために働かず、違法なキャスティングを除いて投げた。

しかし、この暗黙のキャストはうまくいきました:

Item<K>[] array = new Item[SIZE];

ここで、Itemはメンバを含む定義済みのクラスです。

private K value;

このようにして、タイプKの配列(アイテムに値がある場合)またはクラスItemに定義する任意のジェネリックタイプを取得します。


型の安全性を維持しながら、ジェネリックを使って、探しているタイプの配列を正確に取得する方法は次のとおりです(コンパイル時にObject配列や結果を警告する)

import java.lang.reflect.Array;  

public class GenSet<E> {  
    private E[] a;  

    public GenSet(Class<E[]> clazz, int length) {  
        a = clazz.cast(Array.newInstance(clazz.getComponentType(), length));  
    }  

    public static void main(String[] args) {  
        GenSet<String> foo = new GenSet<String>(String[].class, 1);  
        String[] bar = foo.a;  
        foo.a[0] = "xyzzy";  
        String baz = foo.a[0];  
    }  
}

これは警告なしでコンパイルされ、 mainで見ることができるように、 GenSetインスタンスを宣言するどのような型でも、その型の配列にaを代入しaからその型の変数に要素を割り当てることができます。これは配列と配列の値が正しい型であることを意味します。

これは、 Javaチュートリアルで説明したように、クラスリテラルを実行時型トークンとして使用して動作します。 クラスリテラルは、 java.lang.Classインスタンスとしてコンパイラによって処理されjava.lang.Class 。 これを使用するには、 .classを持つクラスの名前に従います。 したがって、 String.classはクラスString表すClassオブジェクトとして機能します。 これは、インタフェース、列挙型、任意の次元の配列( String[].class int.class )、プリミティブ( int.class )、キーワードvoid (ie void.class )でも機能します。

Class自体は総称であり( Class<T>として宣言されていますTClassオブジェクトが表す型を表します)、 String.classの型がClass<String>であることを意味します。

したがって、 GenSetのコンストラクタを呼び出すたびに、 GenSetインスタンスの宣言された型( GenSet<String> String[].class GenSetなど)の配列を表す最初の引数のクラスリテラルをGenSetます。 プリミティブは型変数に使用できないため、プリミティブの配列を取得することはできません。

コンストラクタ内で、メソッドcast呼び出すと、渡されたObject引数のキャストが、メソッドが呼び出されたClassオブジェクトによって表されるクラスに返されます。 java.lang.reflect.Array静的メソッドnewInstance呼び出すと、最初の引数として渡されたClassオブジェクトによって表される型の配列で、 int指定された長さの配列が2番目の引数として渡されます。 メソッドgetComponentType呼び出すと、メソッドが呼び出されたClassオブジェクトによって表される配列のコンポーネント型を表すClassオブジェクトが返されます(たとえば、 String.classClassオブジェクトが配列を表さない場合はnull ) 。

その最後の文は完全に正確ではありません。 Calling String[].class.getComponentType()は、クラスString表すClassオブジェクトを返しますが、その型はClass<String>ではなくClass<String> Class<?>であり、次のようなことはできません。

String foo = String[].class.getComponentType().cast("bar"); // won't compile

Classオブジェクトを返すClass内のすべてのメソッドについても同じです。

この答えに対する Joachim Sauerのコメント(私はそれ自身でコメントする評判は不十分です)に関して、 T[]へのキャストを使用する例では、コンパイラが型の安全を保証できないため警告が表示されます。

Ingoのコメントに関する編集:

public static <T> T[] newArray(Class<T[]> type, int size) {
   return type.cast(Array.newInstance(type.getComponentType(), size));
}

多分この質問に無関係ですが、私は " generic array creation "エラーを使用していましたが

Tuple<Long,String>[] tupleArray = new Tuple<Long,String>[10];

@SuppressWarnings({"unchecked"})を使って次の作品を見つけて(私のために働いた)

 Tuple<Long, String>[] tupleArray = new Tuple[10];

実際には簡単な方法は、オブジェクトの配列を作成し、次の例のように目的の型にキャストすることです。

T[] array = (T[])new Object[SIZE];

SIZEは定数であり、 Tは型識別子である


私は、私のために働く、すばやく簡単な方法を見つけました。 Java JDK 8でこれを使用していることに注意してください。以前のバージョンでも動作するかどうかはわかりません。

特定の型パラメータのジェネリック配列をインスタンス化することはできませんが、すでに作成された配列をジェネリッククラスコンストラクタに渡すことができます。

class GenArray <T> {
    private T theArray[]; // reference array

    // ...

    GenArray(T[] arr) {
        theArray = arr;
    }

    // Do whatever with the array...
}

今度はmainで次のように配列を作成できます:

class GenArrayDemo {
    public static void main(String[] args) {
        int size = 10; // array size
        // Here we can instantiate the array of the type we want, say Character (no primitive types allowed in generics)
        Character[] ar = new Character[size];

        GenArray<Character> = new Character<>(ar); // create the generic Array

        // ...

    }
}

配列の柔軟性を高めるために、リンクされたリストを使用することができます。 ArrayListおよびJava.util.ArrayListクラスにあるその他のメソッド


私はこの問題の解決策のようなものを見つけました。

以下の行は、一般的な配列作成エラーをスローします

List<Person>[] personLists=new ArrayList<Person>()[10];

しかし、 List<Person>を別のクラスにカプセル化すると、それは機能します。

import java.util.ArrayList;
import java.util.List;


public class PersonList {

    List<Person> people;

    public PersonList()
    {
        people=new ArrayList<Person>();
    }
}

PersonListクラスのメンバーをゲッターを介して公開することができます。 下の行は、すべての要素にList<Person>を持つ配列を与えます。 つまり、 List<Person>配列です。

PersonList[] personLists=new PersonList[10];

私が取り組んでいるいくつかのコードでは、このようなものが必要でした。これが動作するようにしたのです。 これまでのところ問題はありません。


private E a[];
private int size;

public GenSet(int elem)
{
    size = elem;
    a = (E[]) new E[size];
}




instantiation