泛型

因为设计者不知道编程者需要把什么样的类型对象放入集合中,所以为了通用性,把集合设计成能保存任何类型的对象。这就导致了一旦把对象放入集合,则对象变为object类型。(运行时类型没变)

这样会引起:集合对元素类型没有限制,可能会引起异常。

      集合丢失了对象的状态信息,集合只知道是object,取出对象时,需要强制类型转换。增加了编程的复杂度,容易引发ClassCastException。

  public static void main(String[] args) {
        var hashset=new HashSet();

        hashset.add(new Person("张三", 14, false));
        hashset.add(new student("李四",7,true));
        hashset.add(new dog());
        for(var it:hashset){
            var p=(Person)it;
            p.sayHi();
        }


Hi,my name is 李四!
Exception in thread "main" java.lang.ClassCastException: class Generic.dog cannot be cast to class Generic.Person (Generic.dog and Generic.Person are in unnamed module of loader 'app')
	at Generic.HashSetErr.main(HashSetErr.java:50)

  如上面代码,就发生了异常,无法将dog类型转为person类型。但是还是可以放进去集合中。

java5以后,引入了泛型,这使得集合可以指定元素的类型。这样就不用强制转换,也不用担心不同类型的对象放入集合中去。

泛型:允许在定义类、接口、方法时使用类型形参,这个类型形参叫做泛型。将在声明变量、创建对象、调用方法时动态地指定(即传入实际的类型参数,也可称为类型实参。

可以为任何类,接口增加泛型声明。

当为类增加时,其构造函数仍然为类名,不加尖括号。但是调用构造函数时候,需要加。

当为泛型类派生子类或者为泛型接口产生实现类时,不能再使用类型形参,必须使用类型实参。

也可以什么也不加,这种形式叫原始类型(raw type),这是,编译器会警告:使用了未经检查或不安全的操作。

只有对类进行泛型声明,并没有泛型类。

 HashSet<String>hashsetStr=new HashSet<>();
 HashSet<Integer>hashsetInt=new HashSet<>();  
 System.out.println(hashsetInt.getClass()==hashsetStr.getClass());



true

 两个hashset的泛型约束不同,但是他们是同一个haseset类。编译器并没有产生两个class文件。

因此,声明了泛型的类,具体的实例是什么类型的实参,与类无关,与实例有关,因此,不允许在类的静态成员中定义泛型相关的东西。因为静态成员只有一份,无法满足泛型的各种不同的类型实参。

类型通配符

如果Foo是Bar的子类或者子接口,G是具有泛型声明的类或者接口,那么G<Foo>和G<Bar>没有父子关系。但是Foo[]是Bar[]的子类。Foo[]自动向上转型为Bar[]的方式称为型变,java数组支持型变,但是集合并不支持。

但是,如果将Bar类型的元素,放入向上转型的Foo[]的数组中,虽然看起来已经变成Bar[]但是仍然会引发异常。所以这是java老版本的一个不安全的设计。在集合中已经改掉。但是可以将G<Foo>赋值给G<? extend Bar>这种叫做协变。

也可以将G<Bar>赋值给G<? super Foo> 这叫做泛型的逆变。

对于协变的泛型,它只能调用泛型类型作为返回值类型的方法,该方法的返回值当成通配符上限类型。不能调用泛型类型作为参数的方法。 协变只出不进。

对于逆变的泛型,它只能调用泛型类型作为参数的方法,不能调用泛型类型作为返回值的方法。 逆变只进不出。

同样可以为泛型的型参设置上限,并且可以多个上限,只有一个父类和多个接口。接口要写在父类后面。

java泛型的设计原则是,只要在编译时没有出现警告,就不会遇到ClassCastException。

List<?>表示各种泛型list的父类。

但是不能把元素加入其中。因为不知道是什么类型。

若不想list<?>表示所有泛型的lis t,则可以上限。例如List<? extends Shape>就表示了list集合的所有元素为Shape的子集。因此当遍历出来的对象就不是object了。若使用list<?>遍历,则都是object。同样不能为有上限的类型通配符的泛型添加对象或者其子类。

因此,指定通配符的上限,就是为了支持类型型变。因为Lis<? extends Shape>可以表示Shape的任何子类的List集合。

<? suprer 类型>通配符下限正好与上限相反。对于逆变的泛型集合,编译器只知道集合元素时下限的父类型,但具体哪个父类不清楚。因此,可以向其中添加元素。只要是下限的父类即可,类型是明确的。

泛型方法

当方法中的一个或多个参数直接存在依赖关系或者参数与返回值之间有依赖关系则应该用泛型方法。否则应该用类型通配符。

换句话说如果泛型方法的t只出现一次,则应该用类型通配符。

泛型构造器

可以在构造器中使用泛型,但是避免使用通配符的上下限来重载方法,这样会引起错误。

类型推断

在可以推断出类型的情况下可以省略<>.

擦除和转换

当把LIst<String>赋值给List时,会将String类型擦除,变为Object类型。

java允许将list变量赋值给list<t>变量,编译时只会做出警告。

然而当实际运行时,如果list实际引用的对象与泛型的实际类型不符,将引发异常。

泛型与数组

1.数组原素的类型,不能包含泛型变量或者泛型数组

2.不能创建元素类型是泛型的数组

3可以创建无上限的通配符泛型数组。

原文地址:https://www.cnblogs.com/tomato190/p/12397067.html