Java泛型

6

我想实现一个方法,它以Object作为参数,将其转换为任意类型,并在失败时返回null。这是我目前的代码:

public static void main(String[] args) {
    MyClass a, b;
    a = Main.<MyClass>staticCast(new String("B"));
}

public static class MyClass {
}

public static <T> T staticCast(Object arg) {
    try {
        if (arg == null) return null;
        T result = (T) arg;
        return result;
    } catch (Throwable e) {
        return null;
    }
}

很遗憾,在staticCast()函数的主体中从未抛出/捕获类转换异常。似乎Java编译器生成了函数String staticCast(Object arg),其中有一行String result = (String) arg;,即使我明确指定模板类型应为MyClass。有什么帮助吗?谢谢。

顺带一提,你应该只捕获 ClassCastException,而不是 Throwable。捕获 Throwable 可能会引起严重的问题。 - Michael Myers
5个回答

10

由于泛型信息在运行时被擦除,将类型转换为泛型类型的标准方式是使用 Class 对象:

public static <T> T staticCast(Object arg, Class<T> clazz) {
    if (arg == null) return null;
    if (!clazz.isInstance(arg))
        return null;
    T result = clazz.cast(arg);
    return result;
}

那么就这样调用它:
a = Main.staticCast("B", MyClass.class);

使用 Class.isInstance 会更直接。 - Tom Hawtin - tackline
“hmm,clazz?那是像klass吗?” - Iraimbilanja
isInstance 只有在对象是 clazz 的实例时才能工作,而不是它的子类。isAssignableFrom 将适用于任何可以转换为 clazz 的内容,甚至包括子类。 - Paul Tomblin
@Paul:Javadocs 中写道,“该方法是 Java 语言 instanceof 运算符的动态等效物。如果指定的 Object 参数非空且可以强制转换为由此 Class 对象所表示的引用类型,则该方法返回 true,否则会引发 ClassCastException 异常。” - Michael Myers

6
你不能按照你想要的方式进行操作......至少不是这种方式。编译器删除了所有信息,所以在转换中(T)变成了Object,并且它能够正常工作。
问题在于后来你执行MyClass = (String)object;,而这是不允许的。
你已经绕过了编译器的检查,导致在运行时出现错误。
那么你究竟想要做什么?如果你告诉我们为什么想要这样做,我们可能会告诉你Java的做法。
根据你的评论,你可以尝试类似这样的东西:
public class Main
{
    public static void main(String[] args)
    {
        String a;
        MyClass b;
        a = staticCast(new String("B"), String.class);
        b = staticCast(new String("B"), MyClass.class);
        System.out.println(a);
    }

    public static class MyClass
    {
    }

    public static <T> T staticCast(Object arg, final Class clazz)
    {
        // edit - oops forgot to deal with null...
        if(arg == null)
        {
            return (null);
        }

        // the call to Class.cast, as mmeyer did, might be better... probably is better... 
        if(arg.getClass() != clazz)
        {
            // Given your answer to Ben S...
            return (null);

            // throw new ClassCastException("cannot cast a " + arg.getClass() + " to a " + clazz);
        }

        return ((T)arg);
    }
}

我明白了,所以T是强制转换中的一个对象。我想为每个T创建一个单独的方法,将参数转换为(T),如果失败则捕获异常而不抛出。根据Paul Tomblin(下面的帖子)的说法,这是不可能实现的。 - Budric

4
你是指类似于 Class<T>.cast(Object obj) 这样的吗?
类似于以下内容:
Class.forName("YourClass").cast(obj);

这应该基本上符合你的要求。

但要注意,这种方法可能存在一定的问题,通常是设计不良的迹象。


谢谢。问题在于如果失败,这将抛出类转换异常。它与代码(YourClass)对象相同,并且在错误时会抛出异常,您必须每次使用该行代码时都进行检查。我想在cast()方法内捕获和隐藏该异常。 - Budric
你想要使用asSubclass还是getConstructor().newInstance().cast? - Tom Hawtin - tackline

1
不要将Java泛型与C++模板混淆。只会有一个staticCast方法,它将使用不同的类型擦除进行调用,而不是为每个T都有一个方法。
如果完全不使用泛型,我会这样写:
public static void main(String[] args) {
    MyClass a, b;
    a = (MyClass)Main.staticCast(new String("B"), MyClass.class);
}

public static class MyClass {
}

public static staticCast(Object arg, Class class) {
        if (arg != null && class.isAssignableFrom(arg))
          return arg;
        return null;
}

或者从另一个答案中借鉴,使用泛型:

public static <T> T staticCast(Object arg, Class<T> class) {
        if (arg != null && class.isAssignableFrom(arg))
        {
           T result = class.cast(arg);
           return result;
        }
        return null;
}

我忘记了CCE是一个RuntimeException,不需要catch块,否则我本来会像你的第二种方法那样做的。 - Michael Myers
你能详细解释一下为什么当我在这行代码中明确指定类型T时,Java编译器不会生成函数MyClass staticCast(Object arg); a = Main.<MyClass>staticCast(new String("B")); 我明确告诉它了...我以为会生成。如果有相关阅读链接就更好了。 - Budric
@Budric:尝试访问http://java.sun.com/docs/books/tutorial/java/generics/erasure.html。此外,搜索标记为[java] [generics]的问题。周围有一些非常有趣的问题。 - Michael Myers
应该使用isInstance而不是isAssignableFrom,并且不需要检查null。 - Tom Hawtin - tackline
请纠正我,但我认为isAssignableFrom会找到给定类的子类,而不仅仅是那种类型的类? - Paul Tomblin

1

我同意Ben的观点,不过我更喜欢以下的表示法:

MyClass.class.cast(obj);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接