java泛型:
- 泛型简介
- 泛型类
- 限制泛型可用类型
- 类型通配声明
- 泛型方法
问题: 如果我们需要产生多个对象,每个对象的逻辑完全一样,只是对象内的成员变量的类型不同。那我们该如何去做?如果按照正常的做法就要创建多个类文件,给每个类中的成员变量设置指定的数据类型。这样做的缺点: 这样会导致类膨胀,重用太差。解决方法: 创建一个类文件,给这个类中2成员变量设置Object(是所有类的父类)数据类型。 缺点: 编译的时候正常,但运行的时候可能会出现异常。因为Object导致整个类对变量的属性很模糊。面对这样的问题在JDK1.5以后引入泛型 。
class Cla1
{
Object a;
public Cla1(Object a) {
this.a=a;
}
public Object getData(){
return a;
}
}
//class cla2
//{
// String a;
// public cla2(String a) {
// this.a=a;
// }
// public String getData(){
// return a;
// }
//}
public class Mian {
public static void main(String[] args) {
Cla1 c1=new Cla1("String");
System.out.println(c1.getData());
//System.out.println((Integer)c1.getData());Object导致整个类对变量的属性很模糊,在强制类型转换的时候会出现下列错误
//Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
//at Mian.main(Mian.java:25)
}
}
泛型简介:
- 泛型可以在编译的时候检查类型安全,并且所有的强制转换都是自动的和隐式的。
- 泛型的原理就是 类型的参数化,即把类型看做参数。也就是说把所有要操作的数据类型看做参数,就像方法的形式参数是运行时传递的值的占位符一样。
- 简单的说,类型变量 扮演的角色就如同一个参数,它是提供给编译器用来类型检查的信息。
- 泛型可以提高代码的扩展性和重用性。
class Cla1<T>//T必须是引用类型
{
T a;
public Cla1(T a) {
this.a=a;
}
public T getData(){
return a;
}
}
public class Mian {
public static void main(String[] args) {
Cla1<Integer> c1=new Cla1<Integer>(10);//<Integer>明确类中参数的类型,相当于类型的参数化,用<>表示
//Cla1<int> c1=new Cla1<int>(10);这样写会报错,<>括号内的必须是引用类型,
System.out.println(c1.getData());
}
}
泛型类及特点:
- 泛型的类型参数可以是泛型类
- 泛型类可以同时设置多个类型参数
- 泛型类可以继承泛型类
- 泛型类可以实现泛型接口
泛型类可以同时设置多个类型参数
class Cla1<T> //一个类型参数
{
T a;
public Cla1(T a) {
this.a=a;
}
public T getData(){
return a;
}
}
class Cla2<T1,T2> //多个类型参数
{
T1 a;
T2 b;
public Cla2(T1 a,T2 b) {
this.a=a;
this.b=b;
}
public T1 getDataA(){
return a;
}
public T2 getDataB(){
return b;
}
}
public class Mian {
public static void main(String[] args) {
Cla1<Integer> c1=new Cla1<Integer>(10);
System.out.println(c1.getData());
Cla2<Integer,String> c2=new Cla2<Integer,String>(30,"输出");
System.out.println(c2.getDataA());
System.out.println(c2.getDataB());
System.out.println(c2.getDataA()+c2.getDataB());//数字和字符串相加,+起连接作用
Cla2<Integer,Integer> c3=new Cla2<Integer,Integer>(100,120);
System.out.println(c3.getDataA());
System.out.println(c3.getDataB());
System.out.println(c3.getDataA()+c3.getDataB());//数字和数字相加,+就是数值相加 //有的博客说这里的数值不能直接相加
}
}
泛型类型可以是泛型类:
class Cla1<T> //一个类型参数
{
T a;
public Cla1(T a) {
this.a=a;
}
public T getData(){
return a;
}
}
public class Main {
public static void main(String[] args) {
Cla1<Cla1<Integer>> c1=new Cla1<Cla1<Integer>>(new Cla1<Integer>(10));
//泛型类里面的类型还是泛型类,传参的时候传的是泛型类的引用
System.out.println(c1.getData().getData());
//c1.getData()是调用的当做参数传入的泛型类,Cla1<Integer> a,就相当于这样的
//此时a是泛型类类型,需要再对a取内容才能得到真正的a的数值,也就是当做参数传进去的那个泛型类的构造方法的数值
}
}
泛型类可以继承泛型类:
abstract class Cla1<T> //一个类型参数
{
T a;
public Cla1(T a) {
this.a=a;
}
public T getData(){
return a;
}
abstract void printInfo();
}
class Cla2<T,T2> extends Cla1<T> //Cla2继承Cla1同时还可以增加泛型的个数,这里增加了T2
{
T2 b;
public Cla2(T a,T2 b) {
super(a);
this.b=b;
}
public T2 getDataB(){
return b;
}
@Override
void printInfo() {//如果这个类也是抽象类的话,就不用在将父类里面的抽象方法给补全了
System.out.println("继承的类是泛型抽象类,所以要在子类里面实现抽象方法的方法体");
}
}
public class Main {
public static void main(String[] args) {
Cla2<Integer,String> c2=new Cla2<Integer,String>(110,"String");
System.out.println(c2.getData());
System.out.println(c2.getDataB());
c2.printInfo();
}
泛型类可以实现继承泛型接口:
interface Cla1<T>
{
abstract void printfInfo(T t);//在接口中public和abstract可以省略,默认就是public和abstract
}
class Cla2<T> implements Cla1<T>
{
public void printfInfo(T t)
{
System.out.println("泛型类继承泛型接口");
}
}
public class Main {
public static void main(String[] args) {
Cla2<String> c1=new Cla2<String>();
c1.printfInfo("");
}
}
限制泛型可用类型:
- 在定义泛型类别的时候,默认在实例化泛型类的时候可以使用任何类型,但是如果要限制使用泛型类型时,只能用某个特定类型或者是其子类型才能实例化该类型时,可以在定义类型时,使用extends关键字指定这个类型必须是继承某个类或者实现某个接口。例如:
class Cla11<T estends Move>
这里Move是接口interface move
这就要求T是接口Move类型的。接口是一个模版,需要类来继承这个接口来实现才能实例化,所以说继承了Move这个接口的类都属于T extren Move。这里就要求是继承了Move接口的类的类型。class Cla1<T extends Animal>
这里Animal是自己定义的一个类,那么T extends Animal
表示T只能是Animal类型或者其子类类型。 - 当没有指定泛型继承的类型或者接口时,默认使用
extends Object
,所以默认情况下任何类型都可以作为参数传入。
限制使用泛型类型,只能用某个特定类型或者是其子类型才能实例化该类型:
interface Cla1<T extends String>
{
abstract void printfInfo(T t);//在接口中public和abstract可以省略,默认就是public和abstract
}
class Cla2<T extends String> implements Cla1<T>//限制泛型只能用某个特定类型或者是其子类型使用extends继承,限制使用泛型类型为String类型
{
T str;
public Cla2(T str) {
this.str=str;
}
public void printfInfo(T t)
{
System.out.println(str);
System.out.println("泛型类继承泛型接口");
}
}
class Cla3<T extends String,T2 extends Integer > extends Cla2<T>
{
T2 in;
public Cla3(T str,T2 in) {
super(str);
this.in=in;
}
public void printfInfo(T t)
{
System.out.println("父类:"+str+" 子类:"+in);
}
}
public class Main {
public static void main(String[] args) {
Cla2<String> c1=new Cla2<String>("父类");
c1.printfInfo(" ");
Cla3<String,Integer> c2=new Cla3<String,Integer>("子类",10);
c2.printfInfo(" ");
}
}
类型通配符声明:
- 同一泛型类,如果实例化时给定的实际类型不同,则这些实例的类型是不兼容的,不能相互赋值。
- 泛型类实例之间的不见兼容性会带来使用的不方便。我们可以使用泛型通配符
<?>
声明泛型类的变量就可以解决这个问题。 - 和限制泛型的上限相似,同样可以使用
extends
关键字限定通配类型的上限:<? extends 为某种类型>
比如:<? extends String>
- 还可以使用super关键词将通配符匹配类型限定为某个类型及其父类型
class Animal
{}
class Dog extends Animal
{}
class Cla1<T>
{
T a;
public Cla1(T a) {
this.a=a;
}
T getData(){
return a;
}
}
public class Main {
public static void main(String[] args) {
Cla1<String> c1=new Cla1<String>("String");
System.out.println(c1.getData());
Cla1<Double> c2=new Cla1<Double>(1.23);
System.out.println(c2.getData());
//c1=c2;不允许这样会报错,因为不是一个类型的
Cla1<?> c3;//使用通配符,即可不用限制类型
c3=c1;
c3=c2;
Cla1<? extends String> c4;
c4=c1;
//c4=c2;因为限制了为String类型所以会报错
Cla1<Dog> c5=new Cla1<Dog>(new Dog());
Cla1<Object> c7=new Cla1<Object>(new Object());
Cla1<? super Dog> c6;//使用super关键词将通配符匹配类型限定为某个类型及其父类型,c6必须是Dog的父类
c6=c5;
Cla1<? super Animal> c8;
c8=c7;
}
}
泛型方法:
- 不仅类可以声明泛型,类中的方法也可以声明仅用于自身的泛型,这种方法叫做泛型方法。其定义格式为:
访问修饰符 <泛型列表> 返回类型 方法名(){}
- 在泛型列表中声明的泛型,可用于该方法的返回类型声明、参数类型声明和方法代码中的局部变量的类型声明。
- 类中的其他方法不能使用当前方法声明的泛型。
- 泛型方法更加灵活,它可以不受泛型类的约束。比方法重载更加灵活。
- 提示: 是否拥有泛型方法,与其所在的类是否是泛型类没有关系,要定义泛型方法,只需要将泛型参数列表置于返回值前。
- 什么时候使用泛型方法,而不是泛型类呢? ① 添加类型约束条件只作用于一个方法的多个参数之间、而不涉及到类中的其他方法时。② 施加类型约束的方法为静态方法,只能将其定义为泛型方法,因为静态方法不能使用其所在类的类型参数。
- 注意: 同样泛型方法也可以对齐进行限制和在泛型类里面的限制一样。T在编译的时候会重置为该类型在类中声明是所继承的父类。比如:
<T> T printInfo(T t)
T默认是继承Object的,所以在编译的时候会重置为Object,而<T extends Animal>
在编译的时候会重置为Animal。
class Animal
{
void eat()
{
System.out.println("动物吃");
}
}
class Dog extends Animal
{
void eat()
{
System.out.println("狗啃骨头");
}
}
class A<T>
{
void printInfo(T t){
System.out.println(t);
}
}
class B
{
<T> T printInfo(T t){//T仅对本方法有效,可用于该方法的返回类型声明和该方法的参数类型声明
T a;//可用于方法代码中的局部变量的类型声明
System.out.println(t);
return t;
}
<T,T2> void printInfo(T t,T2 t2){//一个泛型方法有多个泛型参数,这也是个泛型方法的重载
//System.out.println(t+t2);这样写不允许,必须分开写,因为还没有实例化,类型不确定,不能相加
System.out.println(t);
System.out.println(t2);
}
<T extends Animal> void printInfo2(T t)//施加泛型方法的类型约束
{
t.eat();
}
static <T extends Animal> void printInfo3(T t)//施加泛型方法的类型约束
{
t.eat();
}
}
public class Main {
public static void main(String[] args) {
A<String> a=new A<String>();
a.printInfo("String");
B b=new B();
b.printInfo("String");
b.printInfo('c');
b.printInfo(1213);
b.printInfo(1,"str");
b.printInfo2(new Dog());
b.printInfo2(new Animal());
B.printInfo3(new Animal());
B.printInfo3(new Dog());//静态方法本身就是和类是脱离的,可以直接通过类名来调用静态方法
}
}