34

重複の可能性:
実行時のローカル変数のジェネリック型

私は Java のジェネリックは初めてで、.NET の世界から来たので、次のようなメソッドを記述できることに慣れています。

public void genericMethod<T>(T genericObject)
{
    if (genericObject is IList<String>)
    {
        //Do something...
    }            
}

このメソッドはジェネリック型のオブジェクトを受け入れ、そのオブジェクトがジェネリック インターフェイスの特定IList<>のバージョン (この場合は )を実装しているかどうかをチェックしますIList<String>

今、Javaでは、これを行うことができます:

public <T> void genericMethod(T genericObject)
{
    if (genericObject instanceof Set<?>)
    {
       //Do something...
    }
}

しかし

Javaは私にさせませif (genericObject instanceof Set<String>)

私が知っていることから、型消去のため、通常 Java ではこれはクラス オブジェクトによって処理され、次のようなことを行います。

public <T> void genericMethod(T genericObject)
{
    Class<OurTestingType> testClass = OurTestingType.class;
    if (genericObject.getClass() == testClass)
    {
       //Do something...
    }
}

しかし、私がチェックしているタイプは汎用インターフェースであるため、これを行うことはできません:

Class<Set<String>> testClass = Set<String>.class

では、Java では、ジェネリック オブジェクトが特定の型を実装しているかどうかをどのように確認すればよいのSet<String>でしょうか。

4

2 に答える 2

24

Java は消去を実装しているため、 がgenericObjectのインスタンスであるSet<String>かどうかを実行時に判断する方法はありません。これを保証する唯一の方法は、ジェネリックで境界を使用するか、セット内のすべての要素をチェックすることです。

コンパイル時のジェネリック境界

コンパイル時にチェックされる境界チェックの使用:

public <T extends SomeInterface> void genericMethod(Set<? extends T> tSet) {
    // Do something with tSet here
}

Java 8

Java 8 でストリームを使用して、これを 1 行でネイティブに実行できます。

public <T> void genericMethod(T t) {
    if (t instanceof Set<?>) {
        Set<?> set = (Set<?>) t;
        if (set.stream().allMatch(String.class:isInstance)) {
            Set<String> strs = (Set<String>) set;
            // Do something with strs here
        }
    }
}

Java 7 以前

Java 7 以前では、反復と型チェックを使用する必要があります。

public <T> void genericMethod(T t) {
    Set<String> strs = new HashSet<String>();
    Set<?> tAsSet;
    if (t instanceof Set<?>) {
        tAsSet = (Set<?>) t;
        for (Object obj : tAsSet) {
            if (obj instanceof String) {
                strs.add((String) obj);
            }
        }
        // Do something with strs here
    } else {
        // Throw an exception or log a warning or something.
    }
}

グアバ

以下のMark Petersのコメントによると、プロジェクトに追加できる場合、Guavaにはこれを行うメソッドもあります。

public <T> void genericMethod(T t) {
    if (t instanceof Set<?>) {
        Set<?> set = (Set<?>) t;
        if (Iterables.all(set, Predicates.instanceOf(String.class))) {
            Set<String> strs = (Set<String>) set;
            // Do something with strs here
        }
    }
}

ステートメントIterables.all(set, Predicates.instanceOf(String.class))は、本質的に と同じものですset instanceof Set<String>

于 2012-09-20T15:31:28.333 に答える
11

悲しいことに、Java にはそのオプションがありません。Java では、 aと a の間に実行時の違いはありません。に絶対に変換しないことを保証するのはコンパイラです。そのコンパイラの強制でさえ厳密ではないので、チェックされていないキャストでそのような忌まわしきことを「合法的に」行うことができます....List<String>List<Integer>add()IntegerList<String>

全体として、(ほとんど) 実行時型識別の問題については、List<String>それが実際に何であるかを理解する必要があります: 単なる rawListです。これは型消去と呼ばれます。

Listとはいえ、 a の内容の型を検査することを妨げるものは何もありません。

public boolean isListOf(List<?> list, Class<?> c) {
    for (Object o : list) {
        if (!c.isInstance(o)) return false;
    }

    return true;
}

この方法を使用するには:

    // ...
    if (genericObject instanceof List<?>) {
        if (isListOf((List<?>) genericObject, String.class)) {
            @SuppressWarnings("unchecked")
            List<String> strings = (List<String>) genericObject;
        }
    }

興味深い観察:listが空の場合、メソッドは指定されたすべての型に対して true を返します。List<String>実際には、 emptyと empty List<Integer> whatsoeverの間に実行時の違いはありません。

于 2012-09-20T16:39:52.087 に答える