【问题标题】:Generic method in Java without generic argumentJava中没有泛型参数的泛型方法
【发布时间】:2017-03-10 02:48:36
【问题描述】:

在 C# 中我实际上可以这样做:

//This is C#
static T SomeMethod<T>() where T:new()
{
  Console.WriteLine("Typeof T: "+typeof(T));
  return new T();
}

//And call the method here
SomeMethod<SomeClassName>();

但由于某种原因,我无法让它在 Java 中工作。

我想做的是,在超类上创建一个静态方法,以便子类可以转换为 XML。

//This is Java, but doesn't work
public static T fromXml<T>(String xml) {
  try {
    JAXBContext context = JAXBContext.newInstance(T.class);
    Unmarshaller um = context.createUnmarshaller();
    return (T)um.unmarshal(new StringReader(xml));
  } catch (JAXBException je) {
    throw new RuntimeException("Error interpreting XML response", je);
  }
}

//Also the call doesn't work...
fromXml<SomeSubObject>("<xml/>");

【问题讨论】:

    标签: java generics


    【解决方案1】:
    public static <T> T fromXml(Class<T> clazz, String xml) {
    

    称为:

    Thing thing = fromXml(Thing.class, xml);
    

    或更明确地说:

    Thing thing = MyClass.<Thing>fromXml(Thing.class, xml);
    

    更令人困惑的是,您可以拥有既构造泛型类型又具有自身泛型参数的构造函数。不记得语法并且从未见过它在愤怒中使用(无论如何,您最好使用静态创建方法)。

    演员(T) 是不安全的,你不能写T.class。因此,将 T.class 作为参数包含在内(就像 JAXBContext.newInstance 所做的那样),如果类型错误,则抛出相关异常。

    public static <T> T fromXml(Class<T> clazz, String xml) {
        try {
            JAXBContext context = JAXBContext.newInstance(clazz);
            Unmarshaller um = context.createUnmarshaller();
            Object obj = um.unmarshal(new StringReader(xml));
            try {
                return clazz.cast(obj);
            } catch (ClassCastException exc) {
                 throw new RelevantException(
                     "Expected class "+clazz+
                      " but was "+obj.getClass()
                 );
            }
        } catch (JAXBException exc) {
            throw new RelevantException(
                "Error unmarshalling XML response",
                exc
             );
        }
    }
    

    我相信 JAXB 的下一个版本(在 6u14 中?)在 JAXB 类中为此类事情提供了一些便利方法。

    【讨论】:

    • 甚至不会编译...你不能写 T.class
    • 我不是说在方法中写 T.class。我说作为一个论据。并且调用函数可能没有(或者可能将其作为 argumnet 传递)。
    • newInstance(T.class) 是我没有看到的复制和粘贴。问题似乎仍然是关于如何编写泛型方法。
    • 请将 JAXBContext.newInstance(T.class) 重写为 JAXBContext.newInstance(clazz) 以便我接受。
    • 看看this post。适用于 java 7
    【解决方案2】:

    像 Java 的 Collections.emptySet() 这样的方法有这样的签名:

    public static final <T> Set<T> emptySet()
    

    并且是这样称呼的:

    Set<Foo> foos = Collections.<Foo>emptySet();
    

    Mockito 的anyObject() 方法是另一个例子。我个人认为这两种语法都不是很棒。将类型作为方法参数传递是可行的,但总是感觉很笨拙。以emptySet() 的方式提供参数看起来更简洁,但方法允许指定类型并不总是很明显。

    【讨论】:

      【解决方案3】:

      在 Java 中,泛型是仅编译时的数据,在运行时会丢失。因此,如果您调用这样的方法,JVM 将无法知道T.class 是什么。解决此问题的常规方法是将类实例对象作为参数传递给方法,如下所示:

      public static <T> T fromXml(Class<T> clazz, String xml) {
        try {
          JAXBContext context = JAXBContext.newInstance(clazz);
          Unmarshaller um = context.createUnmarshaller();
          return (T)um.unmarshal(new StringReader(xml));
        } catch (JAXBException je) {
          throw new RuntimeException("Error interpreting XML response", je);
        }
      }
      
      fromXml(SomeSubObject.class, "<xml/>");
      

      【讨论】:

      • 仍然不是写泛型方法的方式。
      • 差不多。 需要紧跟在“静态”之后,而不是我认为的“fromXml”之后。
      • 不理想,不。但是如果他需要在运行时访问类型信息(调用 JAXBContext.newInstance()),他将需要该类的对象,或者类对象。
      【解决方案4】:

      恐怕您正在尝试做的事情在 Java 中根本行不通。无法创建泛型类型的新实例是 .NET 在缺少 Java 时提供的“必备”功能之一。这会导致之前建议的“变通办法”。 查看ArrayList.toArray(T) 作为参考如何做到这一点:本质上,您必须将引用传递给您尝试创建的对象,以便您知道在运行时要实例化哪个类。这是来自ArrayList.toArray(T)的代码:

      public <T> T[] toArray(T[] a) { if (a.length < size) { a = (T[]) java.lang.reflect.Array. newInstance(a.getClass().getComponentType(), size); } System.arraycopy(elementData, 0, a, 0, size); if (a.length > size) { a[size] = null; } return a; }

      【讨论】:

        【解决方案5】:

        当前的答案更安全,但在技术上已经过时,因为在 java7 及更高版本中,您不需要“Class clazz”参数。该类型是从预期的返回类型推断出来的。您只会收到有关不安全演员表的警告。

        public class Main {
        
            private static class Dog {
                public String toString() { return "dog"; }
            }
        
            private static class Cat {
                public String toString() { return "cat"; }
            }
        
            public static void main(String[] args) {
                Cat cat = parse("cat");
                Dog dog = parse("dog");
                System.out.println("the cat object is a " + cat);
                System.out.println("the dog object is a " + dog);
            }
        
            private static Object untypedParse(String stringToParse) {
                if(stringToParse.equals("dog")) {
                    return new Dog();
                } else if(stringToParse.equals("cat")) {
                    return new Cat();
                } else {
                    throw new RuntimeException("not expected");
                }
            }
        
            public static <T> T parse(String stringToParse) {
                return (T)untypedParse(stringToParse);
            }
        
        }
        
        
        ~/test/generics$ javac Main.java
        Note: Main.java uses unchecked or unsafe operations.
        Note: Recompile with -Xlint:unchecked for details.
        ~/test/generics$ java Main
        the cat object is a cat
        the dog object is a dog
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-03-31
          • 1970-01-01
          • 1970-01-01
          • 2012-01-23
          • 2017-10-06
          • 1970-01-01
          相关资源
          最近更新 更多