【问题标题】:Java Generics (simple case, clear enigma for infering)Java 泛型(简单的案例,推理的清晰之谜)
【发布时间】:2012-04-26 15:45:05
【问题描述】:

我有这门课,只是为了学习:

public class MyClass{ //Looking for a solution without making my class also generic <Type>  

    //Private Arraylist var to hold the value called myvar 

   public MyClass(ArrayList<MyDesiredType> incoming) {
        //CODE   myVar=incoming  
    }

    public MyDesiredType getType() {
        return myVar.get(0);
    }   
}

有没有什么方法可以在没有警告和强制转换和失去类型安全性的情况下从构造函数中推断传入对象到方法的返回类型,但最重要的是没有使整个类 GENERIC(对我来说似乎是多余的)?如果不是,为什么我认为这对编译器来说是不可行的?

这是我已经做过的重新表述的问题,但这是我的第一个问题,我学会了如何清楚地揭示它,因为没有人理解。我后来试图编辑原始问题,但一切都被埋没了。我更改并简化了示例并尝试使其简单。原问题:Java Generics Silly Thing (Why cant I infer the type?)

如果有任何问题,请告诉我,我会删除它。

【问题讨论】:

  • 看老问题和答案,似乎很清楚,你想做的事情是不可能的。真正的问题是:你为什么不想让MyClass 通用?
  • 嗯,我只看到一个答案谈到构造函数推断的不可能性,其中一半以上只谈到类中的泛型,而另一个我们都给出了关于推断的矛盾答案。我不想让 MyClass 通用,因为我想知道是否有一些创造性的方法可以从内部传递类型,并且因为我认为如果你已经有了指定类型来指定它来实例化类,这很烦人且不必要。而如果一个普通的方法可以推断,为什么构造函数不能做或者传递给私有变量呢?
  • 实际上有一种情况在理论上是不可能推断出类型的,那就是incomingnull。但是编译器无法知道它是否是null,因此这可能是为什么不允许以这种方式进行类型推断的一个实际参数。
  • 但是使用普通的方法推断时没有问题,其实不是构造函数的问题,我的问题是试图达到并保留构造函数作用域外的类型,分析得很好由用户“101000”
  • 这不是不可能的,我通过强制转换和@supresswarning 得到了它,但我认为它应该在编译器中实现

标签: java generics types constructor inference


【解决方案1】:

不,没有。编译器如何知道要返回什么类型?构造函数中的 ArrayList 的泛型类型在编译期间将是未知的。您要么必须使整个类通用,要么采用其他方法。

考虑一下:

public class Test {
    public static void main(String[] args) {
        List<String> arrList = new ArrayList<String>();
        arrList.add("FOO");
        Test test = new Test(arrList);
        String testStr = test.returnWhat();
        System.out.println("testStr");
    }

    private final List myList; //warning

    public <T> Test(List<T> ttype) {
        myList = ttype;
    }

    public <T> T returnWhat() {
        return (T) myList.get(0); //warning
    }
}

这可行,但会在标记的行上向您发出警告。所以,如果不使整个类通用,真的没有办法实现你所描述的。 因为,如果:

public class Test {


 public static void main(String[] args) {
        List<String> arrList = new ArrayList<String>();
        arrList.add("FOO");
        Test test = new Test(); // now what?
        String testStr = test.returnWhat(0); // no warning...
        JPanel p = test.returnWhat(0); // goes through without warning, real nice...
        test.returnWhat(0); // returns Object

        Test test2 = new Test(arrList);
        test2.addElement(new Object()); // boom, inserted object into list of string.
        String nono = test2.returnWhat(1); // the universe goes down. assign an object to string without warning. even
                                           // though one COULD think the class is generic.
    }

    // private List<T> myList = new ArrayList<T>(); compiler error, T is unknown
    private List myList = new ArrayList();

    public Test() {
        myList.add(new Object());
    }

    public <T> Test(List<T> ttype) {
        myList = ttype;
    }

    public <T> T returnWhat(int index) {
        return (T) myList.get(index);
    }

    public <T> void addElement(T el) {
        myList.add(el);
    }
}

当 myList 被设为通用时,第二个不会编译。在使用默认构造函数的情况下,编译器如何确定 的类型?

此外,这可能会导致依赖于仅插入某些类型这一事实的集合中的对象出现严重问题。

这将产生以下异常:

Exception in thread "main" java.lang.ClassCastException:
java.lang.Object cannot be cast to java.lang.String     at
Test.main(Test.java:27)

我说服你了吗?

真的很好的问题,顺便说一句。我不得不考虑这个问题。

【讨论】:

  • 为什么?如果它适用于方法,为什么不在构造函数中?我可以对方法推断做同样的事情,比如 Java 6 中的 public T getType(T incoming) 并使用它。对于它在 Java Docs 中所说的构造函数请注意,构造函数在泛型和非泛型类中都可以是泛型的(换句话说,声明它们自己的正式类型参数)。考虑以下示例: class MyClass { MyClass(T t) { // ... } }
  • 谢谢你现在我真的看到了,难以置信的答案。再次感谢你!!我的关键在于 addElement 部分比其他部分更多,或者至少是我没有想到的部分。
【解决方案2】:

当您说您希望编译器“在没有警告和强制转换以及失去类型安全性的情况下从构造函数中推断传入对象到方法的返回类型”时,您似乎是在说它应该推断出结果getType() 来自构造函数的输入。如果两者都发生在同一个函数中,它可以。问题是对象可能不只存在于一个函数中,因此需要额外的类型信息(泛型类型)在函数之间传递这种对象。

例如,如果我想编写一个接受MyClass 对象的函数,我需要知道getType() 将返回什么,以便我可以使用返回的值。通过添加MyClass 的泛型类型,我们可以描述它所包含的内容。

另一种看待它的方式是MyClass 是一个容器。通过添加泛型,我们说它是特定类型事物的容器,因此我们可以更轻松地预测我们会从中得到什么。

【讨论】:

  • 抱歉,我的语法可能不正确。我最喜欢你的回答,这就是我所知道的,也是我想知道的。谢谢!
【解决方案3】:

编译器无法在运行时知道您的arraylist 是什么类型。我真的没有看到使用类似这样的东西的问题:

 public class MyClass<TYPE> {
     private ArrayList<TYPE> incoming;

     public MyClass(ArrayList<TYPE> incoming) {
         this.incoming = incoming;
     }

     public TYPE getType() {
         return incoming.get(0);
     }
 }

这样你就可以做到:

ArrayList<Integer> numbers = createListOfNumbers();
MyClass<Integer> myClass = new MyClass<>(numbers);
Integer number = myClass.getType();

或者我误解了这个问题,你想知道运行时的类?

【讨论】:

  • 因为我喜欢这个类的行为方式就像某种工厂,我认为编译器不应该需要类类型来推断它,我认为也许我错了
  • 以我喜欢使用类的方式,我需要手动从 ArrayList 中获取类型以将其作为类型传递以实例化类。想象一下,我在您的第二行中没有信息“Integer”,我收到了 ArrayList 并且应该查找用于实例化我的类的内容,然后在再次将类型传递给 arraylist 之后,为什么需要这样做两次?
  • 这就是类型安全的重点,我认为您错过了泛型的重点。如果你不想推断类型,只是不使用泛型,你可以从 MyClass.getType() 返回的对象中检索类信息
【解决方案4】:

不,如果您想要一个可以保存参数化类型列表的类。

是的,,如果您想要一个可以包含仅一种类型的列表的类。您可以在字段、构造函数和访问器中显式声明该类型。

【讨论】:

  • 谢谢,但我想退货?在哪里 ?是arraylist携带的东西,所以不是具体的类型(它是具体的,因为arralist里面只包含一种类型,但是这个arraylist可以是不同的anytype)。只是认为作为最后的手段使用私有 var 将其作为通配符保存,但他们已经告诉我这是不可能的设计
【解决方案5】:

您忘记的是,并非您可能运行的所有代码对编译器都是可见的!可以在运行时添加、删除、替换 Jar,这是编译器从未见过的。您可以针对以下接口进行编译:

public interface MyClassFactory {
  MyClass getInstance();
}

然后在运行时向 JVM 提供一个实现。因此编译器从未看到创建您将使用的 MyClass 的实际代码,因此无法执行这样的编译时推断。您必须使类成为通用类,或者接受不存在类型安全的事实。

【讨论】:

  • 谢谢,但推理本身是在构造函数中完成的,问题是我无法在构造函数范围之外获取类型,因为没有返回类型,如果我可以在我认为的方法中做到这一点应该有一些成功的方法
  • 关键是编译时间已经完成。如果编译器看不到构造函数调用,但看到你调用了方法(这很容易发生),那么它就无法建立连接。
猜你喜欢
  • 2012-11-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-04
  • 1970-01-01
  • 1970-01-01
  • 2022-01-02
相关资源
最近更新 更多