【问题标题】:How many legal ways are there to create an instance of set/list/map with and without Generics有多少种合法的方法可以创建带有和不带有泛型的 set/list/map 实例
【发布时间】:2014-06-26 15:39:25
【问题描述】:

自从我开始学习泛型以来,我对实例创建有点困惑,因为我意识到,例如,你可以像这样创建一个 HashMap,

HashMap hashmap = new HashMap();

还有,

HashMap<String,String> hashmap = new HashMap<String, String>();

虽然我也可以像这样声明实例 hashmap,因为 HashMap 是从 Map Interface 实现的

Map<String,String> hashmap = new HashMap<String, String>();

但是如果上面是真的,那么我也可以这样创建ArrayList的实例,对吗?

List<String> arraylist = new ArrayList<String>();

还有,自定义一个怎么样,假设类 Walk 从接口 Move 实现,以便创建 Walk 的实例对象,我可以使用两者的代码,

Walk walk = new Walk();
Move walk = new Walk(); 

而且这些不会引起问题,这两个声明是一样的吗?实现是启用该功能的关键特性吗?例如,从另一个类扩展的类,从父类扩展的子类,那么这是合法的吗?

Father son = new Son(); 

【问题讨论】:

    标签: java inheritance implementation


    【解决方案1】:

    好的,这是很多问题和要谈的东西,但我会看看我能在简短的回答中涵盖多少。

    集合声明中的泛型。

    让我们从在声明中使用泛型而不是不使用它们的原因开始。

    取以下两个声明;

    HashMap hashmap = new HashMap();
    HashMap<String,String> hashmap = new HashMap<String, String>();
    

    第一个将接受任何键/值组合,您必须手动使用instanceof 和强制转换从地图中获取对象,因为无法保证它包含您认为的对象类型。

    第二个在声明中使用泛型并确保 String 键和值将在编译时进入您的HashMap 实例。此外,当您调用get() 时,您将自动返回一个String 对象,而不必自己检查和转换它。除非您有充分的理由,否则您应该始终在声明中使用泛型。

    集合声明中的多态性。

    现在在声明集合时讨论多态性。举以下两个例子;

    List<String> sl = new ArrayList<String>();
    ArrayList<String> sl2 = new ArrayList<String>();
    

    第一个更灵活,可以保存任何方法决定返回的任何自定义List 子类。这意味着像 get() 这样的方法可能会返回稍微不同的结果,具体取决于它包含的 List 子类,或者 List 中的项目可能会以不同的顺序保存等。想一想。这为您提供了更大的灵活性,但确定性更少。

    如果您绝对想确保您的 ListArrayList 并且该变量的方法将始终按照 ArrayList 行事(并且始终使用插入顺序),请使用第二个示例。否则,如果您想在 List 类型中允许一些灵活性(例如,如果您希望它能够接受一些有序和排序的列表),请使用第一个。

    我希望这会有所帮助,并且我希望我没有错过任何太重要的事情。

    这里有一些链接可以进一步阅读官方 Java 教程;

    【讨论】:

    • 因此 sl 基本上可以使用 List 中编写的所有 sl2 无法使用的方法?它非常有用,其余的也很有帮助,要么解决了我的问题,要么丰富了我的知识。我读了你的资料,印象深刻,因为你也是自学的,虽然我没能学到更多,所以我在中国参加了一个培训项目,但是,我的老师根本没有真正介绍仿制药和集合,我继续阅读虽然有书,但没有你们我无法理解,对你们所有人都非常感激!无论如何,抱歉发表了一个牵强的评论:D
    【解决方案2】:

    您的第一行 HashMap hashmap = new HashMap() 有效,因为您使用的是原始类型。这将在更新的 Java 版本中引发编译器警告,但它是完全有效的语法;它本质上意味着您可以存储任何类型的键和值,并且不会出现编译器错误。 (This 回答更详细地讨论了原始类型)。

    这与您第二次实例化 HashMap 之间的唯一区别是您对键和值的类型提供了一些限制。

    您其余的所有示例都与继承有关。只要对象实例化的左侧比右侧更通用(即右侧的类型继承自左侧的类型),就不会出现编译器错误。

    您的部分困惑似乎在于实现接口和扩展类之间的区别。对于这个问题,没有区别;你可以用同样的方式实例化。

    【讨论】:

      【解决方案3】:

      使用泛型创建 set/map/list 实例的合法方法:(E 是某个类的对象)

      List<E> arraylist = new ArrayList<E>();
      Map<Integer,E> hashmap = new HashMap<Integer, E>();
      LinkedList<E> list = new LinkedList<E>();
      

      可以使用

      定义泛型
      • 数据类型(整数、双精度等)
      • 参考变量
      • 引用变量数组,
      • 列表、队列甚至地图

      在泛型的帮助下,它更容易,因为您不必在程序中的其他任何地方将类型转换从 Object 类型转换为所需的类型。例如考虑下面的程序: (在没有泛型的情况下创建 set/map/list 实例的合法方法)

      public class ListDemo {
      
          public static void main(String[] args) {
      
              ArrayList courseList = new ArrayList();
              courseList.add("Java");
              courseList.add("J2EE");
              courseList.add("Android");
              courseList.add("Hibernate");
              courseList.add("Spring");
              System.out.println("Displaying the contents: ");
              for (Object obj : courseList)
              {
                  String courseName = (String)obj;
                  System.out.println(courseName);
              }
          }
      }
      

      如果您观察到,需要从 Object 类型转换为 String。因此,当需要将同种数据存储在列表中时,最好使用泛型。

      考虑下面的程序,其中不同类型的数据将存储到一个列表中。然后,使用默认的 Object 类型。

      public class ListDemo {
      
          public static void main(String[] args) {
      
              ArrayList sampleList = new ArrayList();
              sampleList.add("StackOverflow");
              sampleList.add(12345);
              sampleList.add(new Car (2014,"Hyundai","SantaFe"));
              sampleList.add(23.234);
              System.out.println("Displaying the contents: ");
              for (Object obj : sampleList)
                   System.out.println(obj);
          }
      }
      

      另请注意,编译器会发出类型安全警告“方法 add(Object) 属于原始类型 ArrayList。对泛型类型 ArrayList 的引用应参数化”

      阅读更多关于泛型的信息here

      【讨论】:

        【解决方案4】:

        这与泛型无关,都是继承:A a = new B() 在以下情况下有效:

        1. B 是 A(例如,A a = new A()
        2. A 是 B 的祖先(如上面的Father
        3. B 或其祖先之一实现接口 A(如上面的 MapList

        无论是否使用泛型都是如此。

        【讨论】:

        • 明白了,继承不行,因为father也是独立类,谢谢!
        • “它不能在继承中工作”是什么意思?如果Son extends Father 那么代码应该是有效的
        • 类似于从父类扩展的子类和父子 = new Son();错了
        • 我以为你输入了实现,抱歉我一定是太晕了。是的,在实施中怎么样
        • 就您的问题而言,实现接口和扩展类的行为方式相同
        【解决方案5】:

        但是如果上面是真的,那么我也可以这样创建ArrayList的实例,对吗?

        是的,它更好,因为它隐藏了它是一个 ArrayList 的细节。

        还有,自定义一个怎么样,假设类 Walk 从接口 Move 实现,以便创建 Walk 的实例对象,我可以使用两者的代码,

        这些不是通用的;它们只是一个典型的类层次结构。泛型用于您希望用于特定数据类型的集合之类的事物。

        而且这些不会引起问题,这两个声明是一样的吗?

        几乎,只有您只是在第二个声明中公开 Move 的方法。它的意思是“这是一个运动,但你不需要知道它是哪个运动的详细信息。”

        实现是启用该功能的关键功能,而例如从另一个类扩展的类,

        搜索抽象类与接口。部分原因是“是”与“可以”的区别,部分原因是类继承允许您提供实现。那个创建 Java 的家伙开玩笑说他后悔添加继承,说他喜欢接口和组合。

        如果您想要一个很好的类与接口示例,请查看 Collection、List 和 AbstractList。它们的结构非常好,您会发现很少故意使用 AbstractList,除非您将其子类化。

        从父类扩展而来的子类,那么这是合法的吗?

        可能是。如果Father有getChildren()方法,SongetFather()getMother()

        【讨论】:

          猜你喜欢
          • 2016-08-13
          • 1970-01-01
          • 2011-05-03
          • 1970-01-01
          • 2014-12-14
          • 1970-01-01
          • 2023-03-20
          • 1970-01-01
          • 2017-12-25
          相关资源
          最近更新 更多