Java基础 -- 04泛型<T>

目录

泛型概念:

泛型种类:

泛型接口

泛型类

泛型方法

        普通泛型方法

        静态泛型方法

    泛型构造函数

泛型进阶:

    泛型通配符

    泛型上界

    泛型下界

    泛型擦除

泛型注意事项

泛型的用法:

泛型的示例:

高阶篇


java <T> 泛型:死记硬背的

代码中有个  <T> 不要担心,只要编译能通过就OK!

泛型 <T> 定义

定义在类上      class MyClass <T> {}
定义在接口上  interface MyInterface <T> {}
定义在方法上  public <T> T myMethod( T arg) {}

泛型 <T> 运用

运用时,才能明确泛型T到底是个啥类型:
类实例化时      MyClass<String> obj = new MyClass< String>() ; // 明确 T 是 String
方法被调用时  obj.myMethod( "stringxx") ;                                   // 明确 T 是 String(入参arg)

T 类型 的私有数据成员,要求类定义时一定要携带<T>,这样才能编译通过:

public class Test {
    private T name; // 编译报错
}


public class Test<T> {
    private T name; // 编译通过
}

泛型概念:

没有JDK5问世以前的老代码:

  • 先看第1个小示例:Integer类型数值求和a+b,Long类型数值求和a+b
public class GenericTest1
{
	public int sum(int a, int b){
		return a+b;
	}

	public long sum(long a, long b){
		return a+b;
	}
}

我们来分析一下第1个小示例,会发现两个方法除了类型不一样,几乎长得是一摸一样,像双包胎。试问?如果我们还有byte类型的求和,short类型的求和,float类型的求和。。。我们是不是要繁琐的机械的重写很多份这样的方法,这些方法仅仅参数类型和返回类型不一样而已,其它都一样。 那么,我们能不能像模具那样来写方法呢?比如:

public <T> T sum(T a, T b){

      return a+b;

}

这里的T就是个模具,整个方法就是模具式的方法。当你想把T当成byte就当成byte,想把T当成int就当成int,...随心所欲,看你真正使用时想给模具T什么样的真实身份。就是一个模具方法,足矣代表byte类型参数的求和,也能代表short类型的求和,也能代表int类型的求和,等等...恭喜你,当你对此有个认识的时候,你就开始慢慢摸进了泛型方法编码的领域

  • 再看第2个小示例:从ArrayList集合类中取出Integer对象
public class GenericTest2 {

	private ArrayList arr = new ArrayList();

	public void setIntValue(Integer value){
		arr.add(value);
	}

    public void setStrValue(String value){
        arr.add(value);
    }

	public Integer getIntValue(int idx){
		return (Integer)arr.get(idx);
	}
}

我们来分析一下第2个小示例,会发现getIntValue方法的内部,arr.get(i)每次都要进行类型强转(Integer)arr.get(i);

注意:这里是有风险的,因为既可以往arr中存放Integer类型的元素,也可以往arr中存放String类型的元素,所以取出时的类型强转有可能我们取出的元素想强转为Integer,但该元素却是String类型的,此时运行程序时就会报ClassCastException类型转换异常。那么,试问?如果我限定了能放入arr中的只能是Integer类型的元素的话,当我从arr中取出元素时,根本不用类型强制转换,铁定取出的元素是Integer类型的,由此我们可以这样来定义一个模具集合类,请看第2个小示例的改造:

public class GenericTest2 {

    private ArrayList<T> arr = new ArrayList<T>();

    public void setValue(T value){
        arr.add(value);
    }

    public T getIntValue(int idx){
        return arr.get(idx);
    }
}

这里的T就是个模具,整个ArrayList集合类就是模具式的类。当你想把T当成Integer就当成Integer,这样取出的元素铁定是Integer类型的,当你想把T当成String就当成String,这样取出的元素铁定是String类型的...随心所欲,看你真正使用这个模具时想给模具T什么样的真实身份。足矣明确取出的元素类型就是我当初放入时的元素类型,并不需要取出时进行类型强制转换...恭喜你,当你对此有个认识的时候,你就开始慢慢摸进了泛型类编码的领域

JDK5的到来,为我们提供了模具思想的API,只不过JDK5不叫模具,而是叫做泛型。

泛型的定义:参数化类型,也就是说类型是参数化(/模具)的,Java编译后才知道这个类型到底是个啥,类型T仅仅是个模具。泛型的表现形式<T>有尖括号括起来T的整体,单单的一个T并不符合泛型的定义,而仅仅是利用了T而已。创建类型安全的代码,从而在编译时能够捕获类型不匹配错误,这是泛型的一个关键优势,也能体现JDK5引入泛型的意义。

泛型定义时:模具<T> 或 有边界的 <T extends superclass> <T super subclass>

泛型使用时:   

     // 泛型<T> 只能用于定义
     // 泛型<T> 使用:new T(); 非法哦!
    //               需要明确T的类型 (比如: MyCls<Son>)
    //       或者 使用?通配符 (比如: MyCls<?>)
    //       或者 使用?有界通配符 (比如: MyCls<? extends Father>)
    //       或者 仅仅用来做obj的类型强制转换 (比如:(T)obj; )

泛型的思想:定义时模具,使用时明确类型,本质:类类型的校验,是否还要强制转换


泛型种类:有 <T> 是泛型的前提条件

    泛型接口

interface MyInterface<T> {

    // 实现该接口的类,可以仅限定T,class MyClass<T> implements MyInterface<T>{ }
    // 实现该接口的类,可以扩容,class MyClass<T,K> implements MyInterface<T>{ }
}
public interface MyInterface<T> {
    
    // 仅仅使用T的普通方法
    T xxxMethod();       // 返回值类型T
    T xxxMethod(T t);    // 参数类型T,返回值类型T
    void yyyMethod(T t); // 参数类型T,无返回值(这里举例无返回值,其实返回值是啥类型都行)
    
    // <T>的泛型方法
    <T> T aaaMethod();       
    <T> T aaaMethod(T t);    
    <T> void bbbMethod(T t); 
}

public abstract class MyClass<T> implements MyInterface<T>{}   // OK
public abstract class MyClass<T,K> implements MyInterface<T>{} // OK


泛型类

class MyClass<T> {

    // 为什么泛型类?目的:类内部的数据成员是泛型变量的成员,例如:private T a;

    // 该类被实例化时,已经明确了T到底是啥类型,所以编译时能做到类型检查和泛型擦除
}

// 使用时才明确泛型类的MyClass<T>的T到底是个啥

MyClass<String> obj = new MyClass<>(); // T为String

MyClass<Integer> ob

  • 21
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
JavaJava 5引入的新特性,可以提高代码的可读性和安全性,降低代码的耦合度。是将类参数化,实现代码的通用性。 一、的基本语法 在声明类、接口、方法时可以使用的声明方式为在类名、接口名、方法名后面加上尖括号<>,括号中可以声明一个或多个类参数,多个类参数之间用逗号隔开。例如: ```java public class GenericClass<T> { private T data; public T getData() { return data; } public void setData(T data) { this.data = data; } } public interface GenericInterface<T> { T getData(); void setData(T data); } public <T> void genericMethod(T data) { System.out.println(data); } ``` 其中,`GenericClass`是一个类,`GenericInterface`是一个接口,`genericMethod`是一个方法。在这些声明中,`<T>`就是类参数,可以用任何字母代替。 二、的使用 1. 类的使用 在使用类时,需要在类名后面加上尖括号<>,并在括号中指定具体的类参数。例如: ```java GenericClass<String> gc = new GenericClass<>(); gc.setData("Hello World"); String data = gc.getData(); ``` 在这个例子中,`GenericClass`被声明为一个类,`<String>`指定了具体的类参数,即`data`字段的类为`String`,`gc`对象被创建时没有指定类参数,因为编译器可以根据上下文自动推断出类参数为`String`。 2. 接口的使用 在使用接口时,也需要在接口名后面加上尖括号<>,并在括号中指定具体的类参数。例如: ```java GenericInterface<String> gi = new GenericInterface<String>() { private String data; @Override public String getData() { return data; } @Override public void setData(String data) { this.data = data; } }; gi.setData("Hello World"); String data = gi.getData(); ``` 在这个例子中,`GenericInterface`被声明为一个接口,`<String>`指定了具体的类参数,匿名内部类实现了该接口,并使用`String`作为类参数。 3. 方法的使用 在使用方法时,需要在方法名前面加上尖括号<>,并在括号中指定具体的类参数。例如: ```java genericMethod("Hello World"); ``` 在这个例子中,`genericMethod`被声明为一个方法,`<T>`指定了类参数,`T data`表示一个类为`T`的参数,调用时可以传入任何类的参数。 三、的通配符 有时候,我们不知道的具体类,可以使用通配符`?`。通配符可以作为类参数出现在方法的参数类或返回类中,但不能用于声明类或接口。例如: ```java public void printList(List<?> list) { for (Object obj : list) { System.out.print(obj + " "); } } ``` 在这个例子中,`printList`方法的参数类为`List<?>`,表示可以接受任何类的`List`,无论是`List<String>`还是`List<Integer>`都可以。在方法内部,使用`Object`类来遍历`List`中的元素。 四、的继承 类和接口可以继承或实现其他类或接口,可以使用子类或实现类的类参数来替换父类或接口的类参数。例如: ```java public class SubGenericClass<T> extends GenericClass<T> {} public class SubGenericInterface<T> implements GenericInterface<T> { private T data; @Override public T getData() { return data; } @Override public void setData(T data) { this.data = data; } } ``` 在这个例子中,`SubGenericClass`继承了`GenericClass`,并使用了相同的类参数`T`,`SubGenericInterface`实现了`GenericInterface`,也使用了相同的类参数`T`。 五、的限定 有时候,我们需要对的类参数进行限定,使其只能是某个类或接口的子类或实现类。可以使用`extends`关键字来限定类参数的上限,或使用`super`关键字来限定类参数的下限。例如: ```java public class GenericClass<T extends Number> { private T data; public T getData() { return data; } public void setData(T data) { this.data = data; } } public interface GenericInterface<T extends Comparable<T>> { T getData(); void setData(T data); } ``` 在这个例子中,`GenericClass`的类参数`T`被限定为`Number`的子类,`GenericInterface`的类参数`T`被限定为实现了`Comparable`接口的类。 六、的擦除 在Java中,信息只存在于代码编译阶段,在编译后的字节码中会被擦除。在运行时,无法获取的具体类。例如: ```java public void genericMethod(List<String> list) { System.out.println(list.getClass()); } ``` 在这个例子中,`list`的类为`List<String>`,但是在运行时,`getClass`返回的类为`java.util.ArrayList`,因为信息已经被擦除了。 七、的类推断 在Java 7中,引入了钻石操作符<>,可以使用它来省略类参数的声明。例如: ```java List<String> list = new ArrayList<>(); ``` 在这个例子中,`ArrayList`的类参数可以被编译器自动推断为`String`。 八、总结 Java是一个强大的特性,可以提高代码的可读性和安全性,降低代码的耦合度。在使用时,需要注意它的基本语法、使用方法、通配符、继承、限定、擦除和类推断等问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值