关键词:
一. 泛型的使用
1. 泛型类的概述及使用
- A:泛型类概述: 把泛型定义在类上
- B:定义格式: public class 类名<泛型类型1,…>
- C:注意事项: 泛型类型必须是引用类型
2. 泛型方法的概述和使用
- A:泛型方法概述: 把泛型定义在方法上
- B:定义格式: public <泛型类型> 返回类型 方法名(泛型类型 变量名)
public <T> void show(T t) {
}
所谓泛型方法,就是在声明方法时定义一个或多个类型形参。 泛型方法的用法格式如下:
修饰符<T, S> 返回值类型 方法名(形参列表){
方法体
}
注意要点:
- 方法声明中定义的形参只能在该方法里使用,而接口、类声明中定义的类型形参则可以在整个接口、类中使用。当调用fun()方法时,根据传入的实际对象,编译器就会判断出类型形参T所代表的实际类型。
class Demo{
public <T> T fun(T t){ // 可以接收任意类型的数据
return t ; // 直接把参数返回
}
};
public class GenericsDemo26{
public static void main(String args[]){
Demo d = new Demo() ; // 实例化Demo对象
String str = d.fun("汤姆") ; // 传递字符串
int i = d.fun(30) ; // 传递数字,自动装箱
System.out.println(str) ; // 输出内容
System.out.println(i) ; // 输出内容
}
};
3. 泛型接口的概述和使用
先来看一个案例
- A:泛型接口概述: 把泛型定义在接口上
- B:定义格式: public interface 接口名<泛型类型>
/**
* 泛型接口的定义格式: 修饰符 interface 接口名<数据类型> {}
*/
public interface Inter<T> {
public abstract void show(T t) ;
}
/**
* 子类是泛型类
*/
public class InterImpl<E> implements Inter<E> {
@Override
public void show(E t) {
System.out.println(t);
}
}
Inter<String> inter = new InterImpl<String>() ;
inter.show("hello") ;
然后看看源码中泛型的使用,下面是JDK 1.5 以后,List接口,以及ArrayList类的代码片段。
//定义接口时指定了一个类型形参,该形参名为E
public interface List<E> extends Collection<E> {
//在该接口里,E可以作为类型使用
public E get(int index) {}
public void add(E e) {}
}
//定义类时指定了一个类型形参,该形参名为E
public class ArrayList<E> extends AbstractList<E> implements List<E> {
//在该类里,E可以作为类型使用
public void set(E e) {
.......................
}
}
这就是泛型的实质:允许在定义接口、类时声明类型形参,类型形参在整个接口、类体内可当成类型使用,几乎所有可使用普通类型的地方都可以使用这种类型形参。
泛型类派生子类
- 当创建了带泛型声明的接口、父类之后,可以为该接口创建实现类,或者从该父类派生子类,需要注意:使用这些接口、父类派生子类时不能再包含类型形参,需要传入具体的类型。
- 错误的方式:
public class A extends Container<K, V>{}
- 正确的方式:
public class A extends Container<Integer, String>{}
- 也可以不指定具体的类型,此时系统会把K,V形参当成Object类型处理。如下:
public class A extends Container{}
4. 泛型类的概述和使用
定义一个容器类,存放键值对key-value,键值对的类型不确定,可以使用泛型来定义,分别指定为K和V。
public class Container<K, V> {
private K key;
private V value;
public Container(K k, V v) {
key = k;
value = v;
}
public K getkey() {
return key;
}
public V getValue() {
return value;
}
public void setKey() {
this.key = key;
}
public void setValue() {
this.value = value;
}
}
在使用Container类时,只需要指定K,V的具体类型即可,从而创建出逻辑上不同的Container实例,用来存放不同的数据类型。
public static void main(String[] args) {
Container<String,String> c1=new Container<String ,String>("name","hello");
Container<String,Integer> c2=new Container<String,Integer>("age",22);
Container<Double,Double> c3=new Container<Double,Double>(1.1,1.3);
System.out.println(c1.getKey() + " : " + c1.getValue());
System.out.println(c2.getKey() + " : " + c2.getValue());
System.out.println(c3.getKey() + " : " + c3.getValue());
}
在JDK 1.7 增加了泛型的“菱形”语法:Java允许在构造器后不需要带完成的泛型信息,只要给出一对尖括号(<>)即可,Java可以推断尖括号里应该是什么泛型信息。 如下所示:
Container<String,String> c1=new Container<>("name","hello");
Container<String,Integer> c2=new Container<>("age",22);
5. 泛型构造器的概述
- 正如泛型方法允许在方法签名中声明类型形参一样,Java也允许在构造器签名中声明类型形参,这样就产生了所谓的泛型构造器。
- 和使用普通泛型方法一样没区别,一种是显式指定泛型参数,另一种是隐式推断,如果是显式指定则以显式指定的类型参数为准,如果传入的参数的类型和指定的类型实参不符,将会编译报错。
public class Person { public <T> Person(T t) { System.out.println(t); } }
- 如何使用
public static void main(String[] args){ //隐式 new Person(22); //显示 new<String> Person("hello"); }
这里唯一需要特殊注明的就是:
如果构造器是泛型构造器,同时该类也是一个泛型类的情况下应该如何使用泛型构造器:因为泛型构造器可以显式指定自己的类型参数(需要用到菱形,放在构造器之前),而泛型类自己的类型实参也需要指定(菱形放在构造器之后),这就同时出现了两个菱形了,这就会有一些小问题,具体用法再这里总结一下。
以下面这个例子为代表
public class Person<E> {
public <T> Person(T t) {
System.out.println(t);
}
}
这种用法:Person<String> a = new <Integer>Person<>(15);这种语法不允许,会直接编译报错!
二. 泛型高级之通配符
1. 为什么要使用通配符
通配符的设计存在一定的场景,例如在使用泛型后,首先声明了一个Animal的类,而后声明了一个继承Animal类的Cat类,显然Cat类是Animal类的子类,但是List却不是List的子类型,而在程序中往往需要表达这样的逻辑关系。为了解决这种类似的场景,在泛型的参数类型的基础上新增了通配符的用法。
2. <? extends T> 上界通配符
上界通配符顾名思义,<? extends T>表示的是类型的上界【包含自身】,因此通配的参数化类型可能是T或T的子类。
- 正因为无法确定具体的类型是什么,add方法受限(可以添加null,因为null表示任何类型),但可以从列表中获取元素后赋值给父类型。如上图中的第一个例子,第三个add()操作会受限,原因在于List和List是List<? extends Animal>的子类型。
它表示集合中的所有元素都是Animal类型或者其子类
List<? extends Animal>
这就是所谓的上限通配符,使用关键字extends来实现,实例化时,指定类型实参只能是extends后类型的子类或其本身。
- 例如:
- 这样就确定集合中元素的类型,虽然不确定具体的类型,但最起码知道其父类。然后进行其他操作。
//Cat是其子类
List<? extends Animal> list = new ArrayList<Cat>();
3. <? super T> 下界通配符
下界通配符<? super T>表示的是参数化类型是T的超类型(包含自身),层层至上,直至Object
- 编译器无从判断get()返回的对象的类型是什么,因此get()方法受限。但是可以进行add()方法,add()方法可以添加T类型和T类型的子类型,如第二个例子中首先添加了一个Cat类型对象,然后添加了两个Cat子类类型的对象,这种方法是可行的,但是如果添加一个Animal类型的对象,显然将继承的关系弄反了,是不可行的。
它表示集合中的所有元素都是Cat类型或者其父类
List <? super Cat>
这就是所谓的下限通配符,使用关键字super来实现,实例化时,指定类型实参只能是extends后类型的子类或其本身。
例如:
//Shape是其父类
List<? super Cat> list = new ArrayList<Animal>();
4. <?> ***通配符
- 任意类型,如果没有明确,那么就是Object以及任意的Java类了
- ***通配符用<?>表示,?代表了任何的一种类型,能代表任何一种类型的只有null(Object本身也算是一种类型,但却不能代表任何一种类型,所以List和List的含义是不同的,前者类型是Object,也就是继承树的最上层,而后者的类型完全是未知的)。
三. 泛型只能使用引用类型
当声明泛型类的实例时,传递的类型参数必须是引用类型,不能使用基本类型
- 例如,对于 User 泛型类来说,以下声明是非法的
User<int,double> user=new User<>(1,10,0);
如何解决
- 可以使用类型的包装类来解决该问题
《java架构筑基》从java基础讲起——泛型与反射
通过反射获得泛型的实际类型参数把泛型变量当成方法的参数,利用Method类的getGenericParameterTypes方法来获取泛型的实际类型参数例子:publicclassGenericTest{publicstaticvoidmain(String[]args)throwsException{getParamType();}/*利用反射获取方法参数... 查看详情
《java架构筑基》从java基础讲起——泛型基础
一.泛型的概述1.1泛型由来我们的集合可以存储多种数据类型的元素,那么在存储的时候没有任何问题,但是在获取元素,并向下转型的时候,可能会存在一个错误,而这个错误就是ClassCastException.很显然,集合的这种可以存储多种数据类... 查看详情
《java架构筑基》从java基础讲起——基础类型缓存池概念
以Integer为例newInteger(123)与Integer.valueOf(123)的区别在于:newInteger(123)每次都会新建一个对象;Integer.valueOf(123)会使用缓存池中的对象,多次调用会取得同一个对象的引用。Integerx=newInteger(123);Integery=newInteger(123);System.out.println(x==y); 查看详情
《java架构筑基》从java基础讲起——java基础知识
1.Java标识符规则1.1组成规则英文大小写字母数字字符$或_1.2注意事项不能以数字开头不能是Java中的关键字区分大小写不能有空格1.3命名规范【必须见名知意】下划线式命名:my_name驼峰式命名:myName类,接口命名:必须是大写字... 查看详情
《java架构筑基》从java基础讲起——基本数据类型
1.基本类型有哪些Java定义了八种基本数据类型:byte,short,int,long,char,float,double,boolean。基本数据类型也称为简单类型,这些类型可以分为四组:整型。包括byte,short,int,long。用于表示有符号整数。浮点型。包括float,d... 查看详情
《java架构筑基》从java基础讲起——java运算符
1.有哪些运算符Java提供了丰富的运算符,可以将之分为四种:算术运算符、位运算符、关系运算符、逻辑运算符2.算术运算符算术运算符的操作对象必须是数值类型,不能为boolean类型使用算术运算符,但是可以为char类型使用算... 查看详情
《java架构筑基》从java基础讲起——深入理解static
1.static的作用和特点可以用来修饰:成员变量,成员方法,代码块,内部类等。具体如下所示修饰成员变量和成员方法被static修饰的成员属于类,不属于单个这个类的某个对象,被类中所有对象共享,可以并且建议通过类名调用... 查看详情
《java架构筑基》从java基础讲起——string类深入理解
一.String问题答疑String字符串是如何设计与实现考量的?String字符串缓存intern()方法,由永久代移到堆中。String的演化,Java9中底层把char数组换成了byte数组,占用更少的空间二.String的创建机理由于String在Java世界中使用过于频繁,... 查看详情
《java架构筑基》从java基础讲起——常见的api方法
1.Object类1.1Object有哪些公用方法?a.方法equals测试的是两个对象是否相等b.方法clone进行对象拷贝【问题:是浅拷贝还是深拷贝?】c.方法getClass返回和当前对象相关的Class对象d.方法notify,notifyall,wait都是用来对给定对象进行线程同... 查看详情
《java架构筑基》从java基础讲起——深入理解finial
一.final关键字概述1.为什么会有final由于继承中有一个方法重写的现象,而有时候我们不想让子类去重写父类的方法.这对这种情况java就给我们提供了一个关键字:final2.final概述final关键字是最终的意思,可以修饰类,变量,成员方... 查看详情
《java架构筑基》从java基础讲起——基本数据类型存储位置
一.先看一个题目Java中的基本数据类型一定存储在栈中吗?二.答案说明首先说明,"java中的基本数据类型一定存储在栈中的吗?”这句话肯定是错误的。基本数据类型是放在栈中还是放在堆中,这取决于基本类型在何处声明... 查看详情
《java架构筑基》从java基础讲起——int和integer深入分析
1.关于int和Integer的问题区别分析编译阶段、运行时,自动装箱/自动拆箱是发生在什么阶段?使用静态工厂方法valueOf会使用到缓存机制,那么自动装箱的时候,缓存机制起作用吗?为什么我们需要原始数据类型,Java的对象似乎也... 查看详情
《java架构筑基》从java基础讲起——关键字汇总(代码片段)
1.常见的关键字如果还有没有写上的,麻烦小伙伴告知一声……用于定义数据类型的关键字classinterfacebyteshortintlongfloatdoublecharbooleanvoid用于定义数据类型值的关键字truefalsenull用于定义流程控制的关键字ifelseswitchcasedefaultwhiledoforbrea... 查看详情
java基础总结三(泛型异常)(代码片段)
文章目录Java基础总结三(泛型、异常)泛型泛型的创建泛型类泛型接口泛型方法类型擦除泛型的协变与逆变异常异常体系异常处理Java基础总结三(泛型、异常)泛型泛型的创建泛型类我们最常用泛型的地方就是... 查看详情
java基础-泛型的优点
1、性能 对值类型使用非泛型集合类,在把值类型转换为引用类型,和把引用类型转换为值类型时,需要进行装箱和拆箱操作。装箱和拆箱的操作很容易实现,但是性能损失较大。假如使用泛型,就可以避免装箱和拆箱操作... 查看详情
java零基础入门13:java中的泛型(代码片段)
...路线配套文章:Java学习路线总结,搬砖工逆袭Java架构师(全网最强)🍅Java经典面试题大全:10万字208道Java经典面试题总结(附答案)🍅简介:Java领域优质创作者🏆、CSDN哪吒公众号作者✌、Java... 查看详情
java基础java中如何获取一个类中泛型的实际类型
泛型的术语<>:念做typeofList<E>:E称为类型参数变量ArrayList<Integer>:Integer称为实际类型参数ArrayList<Integer>:整个ArrayList<Integer>称为参数化类型(对应着java.lang.reflect.ParameterizedType接口)泛型反射相关APIType[] 查看详情
java泛型的设计
...将会涉及到通配符处理,以及让人苦恼的类型擦除。泛型基础泛型类我们首先定义一个简单的Box类:publicclassBox{privateStringobject;publicvoidset(Stringobject){this.object=object; 查看详情