JAVA泛型
Posted 一只小阿大嗷
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JAVA泛型相关的知识,希望对你有一定的参考价值。
问题引入
问学员一个问题:如果我们需要产生多个对象,每个对象的逻辑完全一样,只是对象内的成员变量类型不同。那我们如何去做?
问题解决
- 创建多个类文件,给每个类中的成员变量设置指定的数据类型。
- 缺点:这种方式会导致类的膨胀,重用性太差。
案例
package 泛型;
class cl1
{
int a;
public cl1(int a)
{
this.a = a;
}
public int getData()
{
return a;
}
}
class cl2
{
String a;
public cl2(String a)
{
this.a = a;
}
public String getData()
{
return a;
}
}
public class Test {
public static void main(String[] args) {
cl1 cl1 = new cl1(120);
cl2 cl2 = new cl2("1200");
System.out.println(cl1.getData());
System.out.println(cl2.getData());
}
}
结果
120
1200
- 创建一个类文件,给这个类中的成员变量设置Object数据类型。
- 缺点:编译的时候正常,但运行的时候可能会异常。
案例
package 泛型;
class cl1
{
Object a;
public cl1(Object a)
{
this.a = a;
}
public Object getData()
{
return a;
}
}
public class Test {
public static void main(String[] args) {
cl1 cl1 = new cl1(120);
cl1 cl2 = new cl1("1200");
System.out.println(cl1.getData());
System.out.println((Integer)cl2.getData());
}
}
结果
120
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
at 泛型.Test.main(Test.java:23)
泛型简介(JDK1.5以后引入)
- 泛型可以在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的。
- 泛型的原理就是“类型的参数化”,即把类型看做参数。也就是说把要操作的数据类型看做参数,就像方法的形式参数是运行时传递的值的占位符一样。
- 简单来说,类型变量扮演的角色就如同一个参数,它提供给编译器用来类型检查的信息。
- 泛型可以提高代码的扩展性和重用性。
示例--泛型类
public class GenClass <T>{
private T obj;
public GenClass (T boj){
this.obj = obj;
}
public T getObj(){
return obj;
}
public void setObj(T obj){
this.obj = obj;
}
}
案例
package 泛型;
class cl1<T>
{
T a;
public cl1(T a)
{
this.a = a;
}
public T getData()
{
return a;
}
}
public class Test {
public static void main(String[] args) {
cl1<Integer> cl1 = new cl1<Integer>(120);
cl1<String> cl2 = new cl1<String>("1200");
System.out.println(cl1.getData());
System.out.println(cl2.getData());
//下面语句不能强转,防止操作失误
//System.out.println((Integer)cl2.getData());
}
}
结果
120
1200
泛型类
特点
- 泛型的类型参数可以是泛型类
- 泛型类可以同时设置多个类型参数
- 泛型类可以继承泛型类
- 泛型类可以实现泛型接口
案例
package 泛型;
//带一个参数的泛型
//可以定义为抽象类,再套一个抽象方法,但这样(1.泛型的类型参数可以是泛型类)就实现不了
/*
abstract class cl1<T>
{
T a;
public cl1(T a)
{
this.a = a;
}
public T getData()
{
return a;
}
abstract void print();
}
*/
class cl1<T>
{
T a;
public cl1(T a)
{
this.a = a;
}
public T getData()
{
return a;
}
}
//4.泛型类可以实现泛型接口
interface cl3<T>
{
abstract void printInfo(T t);
}
//带两个参数的泛型
//3.泛型类可以继承泛型类
class cl2<T,T2> extends cl1<T> implements cl3<T>
{
T2 b;
public cl2(T a,T2 b)
{
super(a);
this.b = b;
}
public T2 getDataT2()
{
return b;
}
@Override
public void printInfo(T t) {
// TODO 自动生成的方法存根
System.out.println("接口printInfo方法输出 "+t);
}
}
public class Test {
public static void main(String[] args) {
cl1<Integer> cl1 = new cl1<Integer>(120);
cl1<String> cl2 = new cl1<String>("1200");
System.out.println(cl1.getData());
System.out.println(cl2.getData());
//下面语句不能强转,防止操作失误
//System.out.println((Integer)cl2.getData());
//1.泛型的类型参数可以是泛型类
cl1<cl1<Integer>> cl = new cl1<cl1<Integer>>(new cl1<Integer>(888));
System.out.println(cl.getData().getData());
//2.泛型类可以同时设置多个类型参数
cl2<Integer,String> cl3 = new cl2<Integer,String>(99,"handsome");
cl2<Integer,Integer> cl4 = new cl2<Integer,Integer>(99,100);
System.out.println(cl3.getData()+cl3.getDataT2());//拼接在一起成99handsome
System.out.println(cl4.getData()+cl4.getDataT2());//两个数字相加成199
int total;
total = cl4.getData()+cl4.getDataT2();
System.out.println("total = "+total);
cl4.printInfo(9999);//这只能Integer,如果要输入字符串或者别的,需要改动接口泛型
/*
比如把接口泛型改成T3,还需要在cl2多加个T3泛型,方法也需要改成T3,定义的时候也得定义T3,定义了String,输出也只能String
把接口泛型改成T3
interface cl3<T3>
在cl2多加个T3泛型
class cl2<T,T2,T3> extends cl1<T> implements cl3<T3>
方法也需要改成T3
public void printInfo(T3 t)
定义的时候也得定义T3
cl2<Integer,String,String> cl3 = new cl2<Integer,String,,String>(99,"handsome");
定义了String,输出也只能String
cl4.printInfo("T3");
*/
}
}
结果
120
1200
888
99handsome
199
total = 199
接口printInfo方法输出 9999
限制泛型可用类型
- 在定义泛型类别时,默认在实例化泛型类的时候可以使用任何类型,但是如果想要限制使用泛型类型时,只能用某个特定类型或者是其子类型才能实例化该类型时,可以在定义类型时,使用extends关键字指定这个类型必须是继承某个类,或者实现某个接口。
- 当没有指定泛型继承的类型或接口时,默认使用extends Object,所以默认情况下任何类型都可以作为参数传入。
案例
package 泛型;
class Animal{}
class Dog extends Animal{}
interface Move
{
abstract void test();
}
class jiekou implements Move
{
@Override
public void test() {
// TODO 自动生成的方法存根
System.out.println("this is test method");
}
}
//限定了T使用类型为Animal,T2的使用类型为String,T3的使用类型为Move(接口)
class cls2<T extends Animal,T2 extends String,T3 extends Move> extends cls1<T>
{
T2 b;
T3 c;
public cls2(T a,T2 b,T3 c)
{
super(a);
this.b = b;
this.c = c;
}
public T2 getDataT2()
{
return b;
}
public T3 getDataT3()
{
return c;
}
}
class cls1<T>
{
T a;
public cls1(T a)
{
this.a = a;
}
public T getData()
{
return a;
}
}
public class Test2 {
public static void main(String[] args) {
cls2<Dog,String,Move> cl1 = new cls2<Dog,String,Move>(new Dog(),"handsome",new jiekou());
//cls2<Dog,String,Integer> cl1 = new cls2<Dog,String,Move>(new Dog(),"handsome",new jiekou());
//边界不匹配:类型 Integer 并不是类型 cls2<T,T2,T3> 的有界参数 <T3 extends Move> 的有效替代项。
System.out.println(cl1.getData());
System.out.println(cl1.getDataT2());
cl1.getDataT3().test();
cls2<Dog,String,Move> cl2 = new cls2<Dog,String,Move>(new Dog(),"kkkkk",new jiekou());
System.out.println(cl2.getData());
System.out.println(cl2.getDataT2());
cl2.getDataT3().test();
}
}
结果
泛型.Dog@15db9742
handsome
this is test method
泛型.Dog@6d06d69c
kkkkk
this is test method
类型通配声明
- 同一泛型类,如果实例化时给定的实际类型不同,则这些实例的类型是不兼容的,不能相互赋值。
Generic<Boolean> f1 = new Generic<Boolean>();
Generic<Integer> f2 = new Generic<Integer>();
f1 = f2;//发生编译错误
Generic<Object> f = f1;//fi和f类型并不兼容,发生编译错误
f = f2;//f2和f类型同样不兼容,也会发生编译错误
- 泛型类实例之间的不兼容性会带来使用的不便。我们可以使用泛型通配符(?)声明泛型类的变量就可以解决这个问题。
类型通配声明 例子
泛型通配的方式
"?"代表任意一个类型
Generic<Boolean> f1 = new Generic<Boolean>();
Generic<?> f = f1;
和限制泛型的上限相似,同样可以使用extends关键字限定通配符匹配类型的上限:
Generic<Dog> f1 = new Generic<Dog>();
Generic<? extends Animal> f = f1;
还可以使用super关键词将通配符匹配类型限定为某个类型及其父类型(下限)
Generic<Animal> f1 = new Generic<Animal>();
Generic<? super Dog> f = f1;
案例
package 泛型通配符;
class Animal{}
class Dog extends Animal{}
class Cat extends Animal{}
class cls1<T>
{
T a;
public cls1(T a)
{
this.a = a;
}
public T getData()
{
return a;
}
}
public class Test {
public static void main(String[] args) {
cls1<Integer> c1 = new cls1<Integer>(10);
cls1<Object> c2 = new cls1<Object>(8);
//c1 = c3;//类型不匹配:不能从 cls1<Object> 转换为 cls1<Integer>
//c3 = c1;//类型不匹配:不能从 cls1<Integer> 转换为 cls1<Object>
//"?"代表任意一个类型
cls1<?> a1;
//和限制泛型的上限相似,同样可以使用extends关键字限定通配符匹配类型的上限
cls1<? extends Dog> a2;
//使用super关键词将通配符匹配类型限定为某个类型及其父类型(下限)
cls1<? super Dog> a3;//必须是Dog的父类
a1 = c1;
//c1 = a1;//类型不匹配:不能从 cls1<capture#3-of ?> 转换为 cls1<Integer>
//a2 = c1;//类型不匹配:不能从 cls1<Integer> 转换为 cls1<? extends Dog>
//a3 = c1;//类型不匹配:不能从 cls1<Integer> 转换为 cls1<? super Dog>
cls1<Dog> s1 = new cls1<Dog>(new Dog());
a1 = s1;
a2 = s1;
a3 = s1;
cls1<Cat> s2 = new cls1<Cat>(new Cat());
a1 = s2;
//a2 = s2;//类型不匹配:不能从 cls1<Cat> 转换为 cls1<? extends Dog>
//a3 = s2;//类型不匹配:不能从 cls1<Cat> 转换为 cls1<? super Dog>
System.out.println("end");
}
}
结果
end
泛型方法
- 不仅类可以声明泛型,类中的方法也可以声明仅用于自身的泛型,这种方法叫做泛型方法。其定义格式为:
访问修饰符 <泛型列表> 返回类型 方法名(参数列表){
实现代码
}
- 在泛型列表中声明的泛型,可用于该方法的返回类型声明、参数类型声明和方法代码中的局部变量的类型声明。
- 类中其他方法不能使用当前方法声明的泛型。
提示:是否拥有泛型方法,与其所在的类是否泛型没有关系。要定义泛型方法,只需将泛型参数列表置于返回值前。
什么时候使用泛型方法,而不是泛型类呢?
- 添加类型约束只作用于一个方法的多个参数之间、而不涉及到类中的其他方法时。
- 施加类型约束的方法为静态方法,只能将其定义为泛型方法,因为静态方法不能使用其所在类的类型参数。
案例
package 泛型方法;
class Bird
{
public void eat()
{
System.out.println("鸟吃虫子");
}
}
class SmallBird extends Bird
{
public void eat()
{
System.out.println("小鸟吃米");
}
}
class BigBird extends Bird
{
public void eat()
{
System.out.println("大鸟啥都吃");
}
}
class Animal<T>
{
public void printInfo(T t)
{
System.out.println(t);
}
}
class Person
{
//访问修饰符 <泛型列表> 返回类型 方法名(参数列表)
public <T> void printInfo(T t)
{
System.out.println(t);
}
//泛型方法的重载
public <T,T2> T printInfo(T t,T2 t2)
{
System.out.println(t);
System.out.println(t2);
//在这不能进行t+t2,因为不确定类型,需要在调用代码的时候确定类型一样即可加。
return t;
}
//方法重载就改变参数类型,泛型对比重载,可以减少代码沉余。
public <T extends Bird> void printInfo2(T t)
{
t.eat();
}
public static <T extends Bird> void printInfo3(T t)
{
t.eat();
}
}
public class Test {
public static void main(String[] args) {
Animal<String> a = new Animal<String>();
a.printInfo("handsome");
Person p = new Person();
p.printInfo("person handsome");
p.printInfo(1234);
p.printInfo(3.14);
p.printInfo('K');
Person p2 = new Person();
//p2.printInfo2("person handsome");//类型 Person 中的方法 printInfo2(T)对于参数(String)不适用
//添加类型约束只作用于一个方法的多个参数之间、而不涉及到类中的其他方法时。
p2.printInfo2(new Bird());
p2.printInfo2(new SmallBird());
//施加类型约束的方法为静态方法,只能将其定义为泛型方法,因为静态方法不能使用其所在类的类型参数。
Person.printInfo3(new BigBird());
}
}
结果
handsome
person handsome
1234
3.14
K
鸟吃虫子
小鸟吃米
大鸟啥都吃
以上是关于JAVA泛型的主要内容,如果未能解决你的问题,请参考以下文章
什么意思 在HashMap之前 ? Java中的泛型[重复]