精华内容
参与话题
问答
  • 为什么需要泛型

    2017-11-24 16:33:16
    想理解泛型必须问一个问题:为什么需要泛型?泛型不就相当于一个未知数吗?java中Object对象就是一个未知数我想传参的时全部用Object接收不就完事了吗? 回答:用Object接收可以的,但是有个缺点,当你要取数据的...

    想理解泛型必须问一个问题:为什么需要泛型?泛型不就相当于一个未知数吗?java中Object对象就是一个未知数我想传参的时全部用Object接收不就完事了吗?

    回答:用Object接收可以的,但是有个缺点,当你要取数据的时候需要强转,你又说了,强转就是的了,我知道放进去的是什么类型的对象,,但是你万一没看准呢,强转错了呢?这时候你的eclispe或者idea不会报错,因为object可以强转成任何对象的,等你tomcat启动的时候才会报错,这时候你还要慢慢找错误。费什么劲,泛型不用强转了,而且如果你去数据的时候接收的类型跟放进去的不一样,马上给你报错,多爽?可以说泛型是Object这个未知数的一个加强,加强的地方是不用强转,写代码的时候接收类型写错了立马报错。


    展开全文
  • .NET泛型或许是借鉴于C++泛型模版,借助它可以实现对... 本篇主要包括:■ 为什么需要泛型 ※ 不用泛型 ※ 使用泛型 ※ 泛型的运行时本质■ 泛型语法■ 典型的泛型类 为什么需要泛型 不用泛型 来看一个比较类型的...

    .NET泛型或许是借鉴于C++泛型模版,借助它可以实现对类型的抽象化、泛型处理,实现了类型和方法之间的解耦。一个最经典的运用是在三层架构中,针对不同的领域模型,在基接口、基类中实现针对各个领域模型的泛型处理。

     

    本篇主要包括:
    为什么需要泛型
        ※ 不用泛型
        ※ 使用泛型
        ※ 泛型的运行时本质
    泛型语法
    典型的泛型类

     

      为什么需要泛型

      不用泛型

    来看一个比较类型的方法。

    public class Calculator
        {
            public static bool AreEqual(int value1, int value2)
            {
                return value1 == value2;
            }
        }


    在客户端调用。

    class Program
        {
            static void Main(string[] args)
            {
                bool result = Calculator.AreEqual(1, 2);
                if (result)
                {
                    Console.WriteLine("相等");
                }
                else
                {
                    Console.WriteLine("不等");
                }
                Console.ReadKey();
            }
        }


    运行结果:不等

     

      不用泛型的缺点一:不是类型安全

    如果我们想使用现在的方法来比较字符串类型。

    bool result = Calculator.AreEqual("A", "B");

    这时,看到编译器报错。从这点来看,AreEqual()方法不是类型安全的方法,当输入string类型,编译器就会报错。

     

    如果把AreEqual()方法的参数类型改成object,编译器就不再报错。

    public class Calculator
        {
            public static bool AreEqual(object value1, object value2)
            {
                return value1 == value2;
            }
        }

    以上,运行也正常。

     

      不用泛型的缺点二:装箱与拆箱导致性能降低

    现在,对于AreEqual(object value1, object value2),从方法本身来讲是没有问题的,但在客户端调用的时候,比如我们还是想比较值类型。

    bool result = Calculator.AreEqual(1, 2);

    在运行时,当整型值类型参数1和2传递、赋值给AreEqual(object value1, object value2)中的引用类型参数value1和value2的时候,发生了一次"装箱"操作。而当把引用类型转换成值类型的时候,又会发生一次"拆箱"操作,这导致性能的降低。

     

      使用泛型

    把AreEqual()改成泛型方法。

    public class Calculator
        {
            public static bool AreEqual<T>(T value1, T value2)
            {
                return value1.Equals(value2);
            }
        }

     

    于是,在客户端可以这样:

    bool result = Calculator.AreEqual<string>("A", "A"); 
    bool result = Calculator.AreEqual<int>(5, 3);

     

    由此,使用泛型的好处有:
    1、实现了方法和类型的解耦。
    2、不会造成类型转换,规避了因装箱于拆箱引起的性能问题。
    3、泛型保证了类型的绝对安全。

     

    当然,还可以把T的位置放在类上:

    public class Calculator<T>
        {
            public static bool AreEqual(T value1, T value2)
            {
                return value1.Equals(value2);
            }
        }

     

    然后这样使用:

    bool result = Calculator<string>.AreEqual("A", "A"); 
    bool result = Calculator<int.AreEqual(1, 2);


     

      泛型的运行时本质

    CLR中有专门的IL指令支持泛型操作。
    →初次编译时,生成IL代码和元数据,T只是类型占位符,在编译时不进行实例化
    →JIT编译时,以实际类型替换元数据中的T占位符
    →将元数据转换为本地代码

     

      泛型语法

    class MyArray<T> where T : Student, new()
    {
        private T[] _items;
        public T myData;
    
        public MyArray()
        {
            myData = default(T);
        }
    
        public void Add(T item)
        {}
    }

    创建泛型实例要指定实际的数据类型:
    MyArray<Int32> myArr = new MyArray<Int32>();

     

    值类型的默认值为0,引用类型的默认值为null,使用泛型默认值:
    myData = default(T);

     

    泛型约束:
    T : 基类名,表示必须是基类名的派生类
    T :new(), 表示必须具有无参构造函数,new()约束必须放在最后面
    T :struct, 表示必须是值类型
    T :class, 表示必须是引用类型
    T :接口名,表示必须实现该接口,或实现该接口的接口

     

    泛型类本质上仍然是一个类,依然可以继承:

    internal class GenericeComparer<T> : Comparer<T> where T : IComparable<T>
    class MyArray<T> : ArrayList

     

      典型的泛型类

    在System.Collections.Generic命名空间和System.Collections.ObjectModel中,定义了不同的泛型类和泛型接口,这些泛型多为集合类。

    List<T> 对应ArrayList集合类
    SortedList<TKey, TValue> 对应SortedList集合类
    Queue<T> 先进先出的集合类
    Stack<T> 后进先出的集合类
    Collection<T> 自定义泛型集合的基类
    Dictionary<TKey, TValue> 对应于Hashtable集合类

     

    参考资料:
    《你必须知道的.NET(第2版)》,作者王涛。

     

    ".NET泛型"系列包括:

    .NET泛型01,为什么需要泛型,泛型基本语法

    .NET泛型02,泛型的使用

    .NET泛型03,泛型类型的转换,协变和逆变

    .NET泛型04,使用Lazy<T>实现延迟加载

    转载于:https://www.cnblogs.com/darrenji/p/3850445.html

    展开全文
  • 为什么需要泛型

    2019-09-16 10:36:34
    泛型概念的提出(为什么需要泛型)? 首先,我们看下下面这段简短的代码: public class GenericTest { public static void main(String[] args) { List list = new ArrayList(); list.add("qqyumidi"); list....

    泛型概念的提出(为什么需要泛型)?

    首先,我们看下下面这段简短的代码:

    public class GenericTest {
    
        public static void main(String[] args) {
            List list = new ArrayList();
            list.add("qqyumidi");
            list.add("corn");
            list.add(100);
    
            for (int i = 0; i < list.size(); i  ) {
                String name = (String) list.get(i); // 1
                System.out.println("name:"   name);
            }
        }
    }

    定义了一个List类型的集合,先向其中加入了两个字符串类型的值,随后加入一个Integer类型的值。这是完全允许的,因为此时list默认的类型为Object类型。在之后的循环中,由于忘记了之前在list中也加入了Integer类型的值或其他编码原因,很容易出现类似于//1中的错误。因为编译阶段正常,而运行时会出现“java.lang.ClassCastException”异常。因此,导致此类错误编码过程中不易发现。

    在如上的编码过程中,我们发现主要存在两个问题:

    1.当我们将一个对象放入集合中,集合不会记住此对象的类型,当再次从集合中取出此对象时,改对象的编译类型变成了Object类型,但其运行时类型任然为其本身类型。

    2.因此,//1处取出集合元素时需要人为的强制类型转化到具体的目标类型,且很容易出现“java.lang.ClassCastException”异常。

    那么有没有什么办法可以使集合能够记住集合内元素各类型,且能够达到只要编译时不出现问题,运行时就不会出现“java.lang.ClassCastException”异常呢?答案就是使用泛型。

    什么是泛型?

    泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。

    看着好像有点复杂,首先我们看下上面那个例子采用泛型的写法。

    public class GenericTest {
    
        public static void main(String[] args) {
            /*
            List list = new ArrayList();
            list.add("qqyumidi");
            list.add("corn");
            list.add(100);
            */
    
            List<String> list = new ArrayList<String>();
            list.add("qqyumidi");
            list.add("corn");
            //list.add(100);   // 1  提示编译错误
    
            for (int i = 0; i < list.size(); i  ) {
                String name = list.get(i); // 2
                System.out.println("name:"   name);
            }
        }
    }

    采用泛型写法后,在//1处想加入一个Integer类型的对象时会出现编译错误,通过List,直接限定了list集合中只能含有String类型的元素,从而在//2处无须进行强制类型转换,因为此时,集合能够记住元素的类型信息,编译器已经能够确认它是String类型了。

    结合上面的泛型定义,我们知道在List中,String是类型实参,也就是说,相应的List接口中肯定含有类型形参。且get()方法的返回结果也直接是此形参类型(也就是对应的传入的类型实参)。下面就来看看List接口的的具体定义:

    public interface List<E> extends Collection<E> {
    
        int size();
    
        boolean isEmpty();
    
        boolean contains(Object o);
    
        Iterator<E> iterator();
    
        Object[] toArray();
    
        <T> T[] toArray(T[] a);
    
        boolean add(E e);
    
        boolean remove(Object o);
    
        boolean containsAll(Collection<?> c);
    
        boolean addAll(Collection<? extends E> c);
    
        boolean addAll(int index, Collection<? extends E> c);
    
        boolean removeAll(Collection<?> c);
    
        boolean retainAll(Collection<?> c);
    
        void clear();
    
        boolean equals(Object o);
    
        int hashCode();
    
        E get(int index);
    
        E set(int index, E element);
    
        void add(int index, E element);
    
        E remove(int index);
    
        int indexOf(Object o);
    
        int lastIndexOf(Object o);
    
        ListIterator<E> listIterator();
    
        ListIterator<E> listIterator(int index);
    
        List<E> subList(int fromIndex, int toIndex);
    }

    我们可以看到,在List接口中采用泛型化定义之后,中的E表示类型形参,可以接收具体的类型实参,并且此接口定义中,凡是出现E的地方均表示相同的接受自外部的类型实参。自然的,ArrayList作为List接口的实现类,其定义形式是:

    public class ArrayList<E> extends AbstractList<E> 
            implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
        
        public boolean add(E e) {
            ensureCapacityInternal(size   1);  // Increments modCount!!
            elementData[size  ] = e;
            return true;
        }
        
        public E get(int index) {
            rangeCheck(index);
            checkForComodification();
            return ArrayList.this.elementData(offset   index);
        }
        
        //...省略掉其他具体的定义过程
    
    }

    由此,我们从源代码角度明白了为什么//1处加入Integer类型对象编译错误,且//2处get()到的类型直接就是String类型了。

    自定义泛型接口、泛型类和泛型方法

    从上面的内容中,大家已经明白了泛型的具体运作过程。也知道了接口、类和方法也都可以使用泛型去定义,以及相应的使用。是的,在具体使用时,可以分为泛型接口、泛型类和泛型方法。

    自定义泛型接口、泛型类和泛型方法与上述Java源码中的List、ArrayList类似。如下,我们看一个最简单的泛型类和方法定义:

    public class GenericTest {
    
        public static void main(String[] args) {
    
            Box<String> name = new Box<String>("corn");
            System.out.println("name:"   name.getData());
        }
    
    }
    
    class Box<T> {
    
        private T data;
    
        public Box() {
    
        }
    
        public Box(T data) {
            this.data = data;
        }
    
        public T getData() {
            return data;
        }
    
    }

    在泛型接口、泛型类和泛型方法的定义过程中,我们常见的如T、E、K、V等形式的参数常用于表示泛型形参,由于接收来自外部使用时候传入的类型实参。那么对于不同传入的类型实参,生成的相应对象实例的类型是不是一样的呢?

    public class GenericTest {
    
        public static void main(String[] args) {
    
            Box<String> name = new Box<String>("corn");
            Box<Integer> age = new Box<Integer>(712);
    
            System.out.println("name class:"   name.getClass());      // com.qqyumidi.Box
            System.out.println("age class:"   age.getClass());        // com.qqyumidi.Box
            System.out.println(name.getClass() == age.getClass());    // true
    
        }
    
    }

    由此,我们发现,在使用泛型类时,虽然传入了不同的泛型实参,但并没有真正意义上生成不同的类型,传入不同泛型实参的泛型类在内存上只有一个,即还是原来的最基本的类型(本实例中为Box),当然,在逻辑上我们可以理解成多个不同的泛型类型。

    究其原因,在于Java中的泛型这一概念提出的目的,导致其只是作用于代码编译阶段,在编译过程中,对于正确检验泛型结果后,会将泛型的相关信息擦出,也就是说,成功编译过后的class文件中是不包含任何泛型信息的。泛型信息不会进入到运行时阶段。

    对此总结成一句话:泛型类型在逻辑上看以看成是多个不同的类型,实际上都是相同的基本类型。

    类型通配符

    接着上面的结论,我们知道,Box和Box实际上都是Box类型,现在需要继续探讨一个问题,那么在逻辑上,类似于Box和Box是否可以看成具有父子关系的泛型类型呢?

    为了弄清这个问题,我们继续看下下面这个例子:

    public class GenericTest {
    
        public static void main(String[] args) {
    
            Box<Number> name = new Box<Number>(99);
            Box<Integer> age = new Box<Integer>(712);
    
            getData(name);
            
            //The method getData(Box<Number>) in the type GenericTest is 
            //not applicable for the arguments (Box<Integer>)
            getData(age);   // 1
    
        }
        
        public static void getData(Box<Number> data){
            System.out.println("data :"   data.getData());
        }
    
    }

    我们发现,在代码//1处出现了错误提示信息:The method getData(Box) in the t ype GenericTest is not applicable for the arguments (Box)。显然,通过提示信息,我们知道Box在逻辑上不能视为Box的父类。那么,原因何在呢?

    public class GenericTest {
    
        public static void main(String[] args) {
    
            Box<Integer> a = new Box<Integer>(712);
            Box<Number> b = a;  // 1
            Box<Float> f = new Box<Float>(3.14f);
            b.setData(f);        // 2
    
        }
    
        public static void getData(Box<Number> data) {
            System.out.println("data :"   data.getData());
        }
    
    }
    
    class Box<T> {
    
        private T data;
    
        public Box() {
    
        }
    
        public Box(T data) {
            setData(data);
        }
    
        public T getData() {
            return data;
        }
    
        public void setData(T data) {
            this.data = data;
        }
    
    }

    这个例子中,显然//1和//2处肯定会出现错误提示的。在此我们可以使用反证法来进行说明。

    假设Box在逻辑上可以视为Box的父类,那么//1和//2处将不会有错误提示了,那么问题就出来了,通过getData()方法取出数据时到底是什么类型呢?Integer? Float? 还是Number?且由于在编程过程中的顺序不可控性,导致在必要的时候必须要进行类型判断,且进行强制类型转换。显然,这与泛型的理念矛盾,因此,在逻辑上Box不能视为Box的父类。

    好,那我们回过头来继续看“类型通配符”中的第一个例子,我们知道其具体的错误提示的深层次原因了。那么如何解决呢?总部能再定义一个新的函数吧。这和Java中的多态理念显然是违背的,因此,我们需要一个在逻辑上可以用来表示同时是Box和Box的父类的一个引用类型,由此,类型通配符应运而生。

    类型通配符一般是使用 ? 代替具体的类型实参。注意了,此处是类型实参,而不是类型形参!且Box在逻辑上是Box、Box...等所有Box<具体类型实参>的父类。由此,我们依然可以定义泛型方法,来完成此类需求。

    public class GenericTest {
    
        public static void main(String[] args) {
    
            Box<String> name = new Box<String>("corn");
            Box<Integer> age = new Box<Integer>(712);
            Box<Number> number = new Box<Number>(314);
    
            getData(name);
            getData(age);
            getData(number);
        }
    
        public static void getData(Box<?> data) {
            System.out.println("data :"   data.getData());
        }
    
    }
    

    有时候,我们还可能听到类型通配符上限和类型通配符下限。具体有是怎么样的呢?

    在上面的例子中,如果需要定义一个功能类似于getData()的方法,但对类型实参又有进一步的限制:只能是Number类及其子类。此时,需要用到类型通配符上限。

    public class GenericTest {
    
        public static void main(String[] args) {
    
            Box<String> name = new Box<String>("corn");
            Box<Integer> age = new Box<Integer>(712);
            Box<Number> number = new Box<Number>(314);
    
            getData(name);
            getData(age);
            getData(number);
            
            //getUpperNumberData(name); // 1
            getUpperNumberData(age);    // 2
            getUpperNumberData(number); // 3
        }
    
        public static void getData(Box<?> data) {
            System.out.println("data :"   data.getData());
        }
        
        public static void getUpperNumberData(Box<? extends Number> data){
            System.out.println("data :"   data.getData());
        }
    
    }
    

    此时,显然,在代码//1处调用将出现错误提示,而//2 //3处调用正常。

    类型通配符上限通过形如Box形式定义,相对应的,类型通配符下限为Box形式,其含义与类型通配符上限正好相反,在此不作过多阐述了。

    话外篇

    本文中的例子主要是为了阐述泛型中的一些思想而简单举出的,并不一定有着实际的可用性。另外,一提到泛型,相信大家用到最多的就是在集合中,其实,在实际的编程过程中,自己可以使用泛型去简化开发,且能很好的保证代码质量。并且还要注意的一点是,Java中没有所谓的泛型数组一说。

    对于泛型,最主要的还是需要理解其背后的思想和目的。

    【感谢您能看完,如果能够帮到您,麻烦点个赞~】

    更多经验技术欢迎前来共同学习交流:一点课堂-为梦想而奋斗的在线学习平台 http://www.yidiankt.com/

    ![关注公众号,回复“1”免费领取-【java核心知识点】]file

    QQ讨论群:616683098

    QQ:3184402434

    想要深入学习的同学们可以加我QQ一起学习讨论~还有全套资源分享,经验探讨,等你哦!在这里插入图片描述

    展开全文
  • 集合容器类在设计阶段/声明阶段不能确定这个容器到底实际存的是什么类型的对象,所以在JDK1.5之前只能把元素类型设计object,JDK1.5之后使用泛型来解决。因为这个时候除了元素的类型不确定,其他的部分是确定的,...

    ·泛型:标签

    ·举例:

    中药店,每个抽屉外面贴着标签

    超市购物架上很多瓶子,每个瓶子装的是什么,有标签

    ·泛型的设计背景

    集合容器类在设计阶段/声明阶段不能确定这个容器到底实际存的是什么类型的对象,所以在JDK1.5之前只能把元素类型设计为object,JDK1.5之后使用泛型来解决。因为这个时候除了元素的类型不确定,其他的部分是确定的,例如关于这个元素如何保存,如何管理等是确定的,因此此时把元素的类型设计成一个参数,这个类型参数叫做泛型。Collection,List,ArrayList这个就是类型参数,即泛型。

    在这里插入图片描述

    一、什么是泛型?

    Java泛型设计原则:只要在编译时期没有出现警告,那么运行时期就不会出现ClassCastException异常.

    泛型:把类型明确的工作推迟到创建对象或调用方法的时候才去明确的特殊的类型

    参数化类型:

    • 把类型当作是参数一样传递
    • <数据类型>只能是引用类型

    相关术语:

    • ArrayList中的E称为类型参数变量
    • ArrayList中的Integer称为实际类型参数
    • 整个称为ArrayList泛型类型
    • 整个ArrayList称为参数化的类型ParameterizedType

    二、为什么需要泛型

    早期Java是使用Object来代表任意类型的,但是向下转型有强转的问题,这样程序就不太安全

    首先,我们来试想一下:没有泛型,集合会怎么样

    • Collection、Map集合对元素的类型是没有任何限制的。本来我的Collection集合装载的是全部的Dog对象,但是外边把Cat对象存储到集合中,是没有任何语法错误的。
    • 把对象扔进集合中,集合是不知道元素的类型是什么的,仅仅知道是Object。因此在get()的时候,返回的是Object。外边获取该对象,还需要强制转换

    有了泛型以后:

    • 代码更加简洁【不用强制转换】
    • 程序更加健壮【只要编译时期没有警告,那么运行时期就不会出现ClassCastException异常】
    • 可读性和稳定性【在编写集合的时候,就限定了类型】

    2.1有了泛型后使用增强for遍历集合

    在创建集合的时候,我们明确了集合的类型了,所以我们可以使用增强for来遍历集合!

    //创建集合对象*        
    ​	ArrayList<String> list = new ArrayList<>();
    
    		list.add("hello"); 
    		list.add("world"); 
            list.add("java"); 
    	//遍历,由于明确了类型.我们可以增强
    		for for (String s : list) { 
        		System.out.println(s); 
    		}
    
    
    
    展开全文
  •  * 功能:泛型的必要性(为什么需要泛型) *好处:编译的时候检查类型安全,并且所有的强制类型转换都是自动和隐式的,提高代码的重用率  */ package com.test; import java.util.ArrayList; public class ...
  • 泛型概念的提出(为什么需要泛型)? 首先,我们看下下面这段简短的代码: 按 Ctrl+C 复制代码按 Ctrl+C 复制代码 定义了一个List类型的集合,先向其中加入了两个字符串类型的值,随后加入一个Integer类型的值。这是...
  • 这篇文章是关于向Go添加泛型的意义,以及为什么我认为我们应该这样做。我还将介绍为Go添加泛型的设计可能的改变。 Go于2009年11月10日发布。不到24小时后,我们看到了关于泛型的第一条评论。(该评论还提到了我们在...
  • 假设需要你定义一个描述坐标的程序类Point,需要提供两个属性x、y。对于这两个属性的内容可能有如下选择: x = 10、y = 20 ; x = 10.1、y = 20.1 ; x = 东经80度、y = 北纬20度 那么现在首先要解决的问题就是...
  • 1. 为什么需要泛型why 2 2. 定义分类what 2 2.1. 编辑类型参数 2 2.2. 模板 2 2.3. 简单理解 占位符代替object、 3 3. 泛型的机制编辑how 3 3.1.1. 机制 3 3.1.2. 编译机制 3 4. 泛型的好处 3 4.1. 提高...
  • 首先,我们看下下面这段简短的代码: public class GenericTest { public static void main(String[] args) { List list = new ArrayList(); list.add("qqyumidi"); list.add("corn");... list.ad
  • 那么其次进行追本朔源说一下为什么需要泛型。在1.0和1.1的时候应该是没有泛型的概念的,当时的出现了集合包的前身,后来到1.2集合包出现了好多种类的集合,为了能让集合复用,使得代码利用最大化,所以使用了Java的...
  • 为什么泛型

    2015-10-07 02:01:56
    泛型概念的提出(为什么需要泛型)? 首先,我们看下下面这段简短的代码: 1 public class GenericTest { 2 3 public static void main(String[] args) { 4 List list = new ArrayList(); 5 list.add...
  • 泛型:参数化类型,即由于类型的不确定性,我们可以引入一个参数来表示代替某种...为什么需要泛型呢?看以下例子: /****/packagecom.skywares.generic;/***@authorAdministrator**/publicclassPrintArray{/***@parama...
  • 为什么设计泛型

    2016-10-12 21:27:40
    1,为什么设计泛型?难道是以前的数据类型不够用吗? 问题:在设计的过程中经常会用到容器类,容器类代码都一样只是数据类型不同,如果能够让一种类型容纳所有类型,就可以实现代码重用,但是没有一种类型可以容纳...
  • 泛型

    2020-03-24 17:24:27
    我们为什么需要泛型? 泛型 泛型方法 泛型类

空空如也

1 2 3 4 5 ... 20
收藏数 1,438
精华内容 575
关键字:

为什么需要泛型