import java.security.CodeSigner;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.FutureTask;
public class GenericClass {
/**
* 为什么使用泛型的痛点一?
* 让同一段逻辑相同的代码适应不同的数据类型
*/
private int add(int a,int b){
//仅适用于int类型
return a + b;
}
private float add(float a, float b){
//仅适用于float类型
return a + b;
}
private double add(double a, double b){
//仅适用于double类型
return a + b;
}
/**
* 以上三个方法中逻辑完全一样,仅仅是因为传入的参数的类型不同,就需要拆分成三个方法。
* 这是泛型需要解决的第一个痛点问题。
*/
private void calculate(){
add(1 , 2);
add(1f,2f);
add(1d,2d);
}
/**
* 为什么使用泛型的痛点二?
* 在list等集合类型中,在往其中插入数据类型时,不会进行类型检查,也不会报错。
* 但是在取出时需要进行类型检查, 并进行强制转换,如果因为转换类型出错,会引发崩溃问题。
*
* 如果在list声明的时候能指定可以插入的数据类型,
* 在插入的时候就进行类型检查,只允许限定的类型插入。
* 在使用时,取出后也不用进行强制类型转换进而引发崩溃问题。
*/
private void list(){
//在一个list中同时插入不同的数据类型,并且不会报任何异常
List list = new ArrayList();
list.add("title");
list.add("name");
list.add("address"); //前三个数据为String类型
list.add(100); //最后一个为int类型
for(int i=0;i<list.size();i++){
//在使用时需要进行强制类型转换,而且会因为类型转换不对,而导致异常
// System.out.println((String)list.get(i) );
}
List<String> listGeneric = new ArrayList<>();
listGeneric.add("title");
listGeneric.add("name");
listGeneric.add("address"); //前三个数据为String类型
// listGeneric.add(100); //最后一个为int类型,插入时会报异常
}
/**
* 使用一个泛型类
*/
private void userAGenericClass(){
//实现泛型类时用Integer类型初始化
GenericClass1<Integer> genericClassInt = new GenericClass1<Integer>();
genericClassInt.c = 12;
genericClassInt.compare(12,15);
//实现泛型类时用Float类型初始化
GenericClass1<Float> genericClassFloat = new GenericClass1<Float>();
genericClassFloat.c = 12F;
genericClassFloat.compare(12F,15F);
//实现泛型类时用Double类型初始化
GenericClass1<Double> genericClassDouble = new GenericClass1<Double>();
genericClassDouble.c = 12D;
genericClassDouble.compare(12D,15D);
}
public static void main(String[] args){
GenericClass genericClass = new GenericClass();
// 为什么使用泛型
genericClass.calculate();
genericClass.list();
//定义并使用一个泛型类
genericClass.userAGenericClass();
//定义并使用一个泛型接口
//使用泛型方法
genericClass.useAGenericMethod();
genericClass.extendsObj();
}
/**
* 定义一个泛型类
* 用T抽象一个类中的某一类型,
* 该类型可用来限定属性、方法的参数以及方法的返回值
* @param <T>
*/
class GenericClass1<T>{
//使用了泛型的属性声明
private T c;
//使用了泛型的方法声明
public void setC(T c) {
this.c = c;
}
//使用了泛型的方法声明
private T compare(T a, T b){
if(a == c){
return a;
}else{
return b;
}
}
}
/**
* 定义一个泛型接口
*/
interface GenericInterface<T>{
void hello(T a);
}
/**
* 泛型接口的使用方式1 ,在实现泛型接口时指定了具体的类型。
* 实现类是一个普通类,实现的方法也是一个普通方法。
* 使用时当成普通类使用。
*/
class GenericInterface1 implements GenericInterface<String> {
@Override
public void hello(String a) {
}
}
/**
* 泛型接口的使用方式2,继续沿用泛型,用一个泛型类实现一个泛型接口。
* 实现类是一个泛型类,实现的接口中的方法也依然使用泛型抽象了具体类型。
* 使用时当成泛型类使用。
* @param <T>
*/
class GenericInterface2<T> implements GenericInterface<T>{
@Override
public void hello(T a) {
}
}
/**
* 定义泛型类和泛型接口时可以指定多个泛型参数
* @param <T>
* @param <V>
* @param <L>
*/
class GenericClassMulti<T,V,L>{
private void helloWorld(T t, V v, L l){
System.out.println(t + " " + v + " " + l);
}
}
interface GenericInterfaceMulti<M,N>{
void hello(M m, N n);
}
/**
* 泛型方法
* 泛型方法和泛型类以及泛型接口是完全独立的,并不是说在泛型类或者泛型接口中声明的方法就是泛型方法,
* 而是必须使用尖括号加泛型参数定义的方法。<T>
*
* 以上的所有方法包括add、hello、helloWorld等虽然有泛型参数,但都不是泛型方法。
*
* 泛型方法中限定的泛型参数,可以应用到函数返回值和参数类型中,同样不限定泛型参数的个数
* @param <T>
*/
private <T> void myName(T level){
//这里可能有好多不知道的逻辑
}
private <K,M> M myName1(K k, M m){
//这里可能有好多不知道的逻辑
return m;
}
/**
* 在泛型类中定义的泛型方法
* @param <P>
*/
class GeneClass<P>{
//这是一个普通方法,仅仅是使用了在泛型类中限定的泛型参数P
private P get(P p){
return p;
}
//这是我一个泛型方法, 这里使用的泛型参数P和泛型类中使用的P不是同一个,两者没有任何关系。
private <P> void getName(P p){
}
}
/**
* 使用泛型方法
*/
private void useAGenericMethod(){
myName("top"); //限定String类型
myName(0); //限定Integer类型
myName(new GenericClass()); //限定自定义的GenericClass类型
myName1("top","t"); //两个泛型参数都限定为String类型
myName1(1,"string"); //分别限定为Integer类型以及String类型
myName1(new GenericClass(),"2132432"); //分别限定为GenericClass类型和String类型
//在泛型类中使用泛型方法
GeneClass<String> geneClass = new GeneClass();
geneClass.get("name"); //必须传入String类型的参数
//泛型方法的使用,和对象初始化时限定的类型无关
geneClass.<String>getName("hi"); //可以传入String
geneClass.<Integer>getName(90); //可以传入Integer类型
geneClass.getName(new GeneClass<Integer>()); //可以传入自定义的类型
}
/**
* 泛型中的约束
*/
class Fruit{
}
class Apple extends Fruit {
}
class Person{
}
private void extendsObj(){
//用Fruit限定泛型参数
GeneClass<Fruit> fruitGeneClass = new GeneClass<>();
fruitGeneClass.get(new Fruit()); //可以传入Fruit对象
fruitGeneClass.get(new Apple()); //可以传入Apple对象,因为Apple继承Fruit。
// fruitGeneClass.get(new Person()); //不可以传入Person对象,因为Person对象与Fruit对象无关
//在泛型类中定义的泛型方法,在使用时限定的类型与对象初始化时限定的类型无关
fruitGeneClass.getName(new Fruit());
fruitGeneClass.getName(new Apple());
fruitGeneClass.getName(new Person());
}
/**
* 限定泛型参数的类型
* @param <T>
*/
class GenericExtends<T extends Comparable>{
//如何保证传入的T类型中含有compareTo()方法。通过让泛型参数继承自Comparable,
// 此时初始化该泛型类时传入的类型必须实现了Comparable接口
private int compare(T a, T b){
return a.compareTo(b);
}
}
/**
* 补充:
* <T extends Comparable> 中extends 后的类型可以是类,也可以是接口,可以有一个,也可以有多个。
* 但是如果同时集成了类和接口,类必须放在第一个,并且类只能有一个,因为Java是单继承的。
* 比如 <T extends Person,Comparable>
*/
/**
* 泛型在使用过程中的约束以及局限性
*/
static class GenericLimit<T> {
//1、不能new一个T
public void instance(){
T t = new T();
}
//2、静态方法或者静态变量中不能使用类型变量。
//因为类型变量是在对象初始化的时候指定的,而静态变量和静态域是在这之前就完成了初始化。
public static T t1;
public static void run(T t){
}
//3、静态方法如果是一个泛型方法是可以的,比如
public static <T> void run1(T t){
}
//基础类型不允许实例化一个泛型参数,必须使用其包装类型。因为double、int等基础类型不是对象
public static void run2(){
// new GenericLimit<double>();
new GenericLimit<Double>();
}
}
}