您的位置:首页 > 科技 > 能源 > 萧山网_怎么样做seo_如何创建一个网站_刷赞网站推广ks

萧山网_怎么样做seo_如何创建一个网站_刷赞网站推广ks

2024/11/15 15:47:08 来源:https://blog.csdn.net/2302_81249757/article/details/143481374  浏览:    关键词:萧山网_怎么样做seo_如何创建一个网站_刷赞网站推广ks
萧山网_怎么样做seo_如何创建一个网站_刷赞网站推广ks

 看到这句话的时候证明:此刻你我都在努力

加油陌生人

个人主页:Gu Gu Study
专栏:用Java学习数据结构系列
喜欢的一句话: 常常会回顾努力的自己,所以要为自己的努力留下足迹

喜欢的话可以点个赞谢谢了。
作者:小闭


目录

前言

泛型的概念

泛型的擦除机制

泛型的上界

通配符上界

我们实现一下场景一:

场景二:

泛型上界与通配符上界的区别

泛型上界:

通配符上界:

通配符下界


前言

本系列准备已经结束,反射,lambda表达示,之类知识了。本系列属于数据结构初阶,进阶的敬请期待。本文章主要是讲泛型的进一步认识,以及更加底层的String类的认识。

之前也写过一篇泛型初阶的一篇文章,大家如果没看过可以再看看。

这篇文章已经讲了:包装类,简单的编译器推导,泛型的基本使用,以及泛型上界。

泛型的概念

泛型是Java中一种强大的特性,它允许程序员在编写代码时指定类型参数,从而使得代码更加灵活和可重用。泛型提供了一种方式,使得编译器可以在编译时检查类型安全,避免了类型转换的错误和运行时的类型检查。

通俗来说: 就是适用于许多许多类型 ,从代码上讲,就是对类型实现了参数化。

语法:

class 泛型类名称<类型形参列表> {

// 这里可以使用类型参数

}

简单示例泛型的简单使用:

class MyArray<T> {  //注释1public Object[] array = new Object[10];public T getPos(int pos) {return (T)this.array[pos];}public void setVal(int pos,T val) {this.array[pos] = val;}
}
public class TestDemo {public static void main(String[] args) {MyArray<Integer> myArray = new MyArray<>();//注释2myArray.setVal(0,10);myArray.setVal(1,12);//注释3int ret = myArray.getPos(1);System.out.println(ret);myArray.setVal(2,"bit");//注释4 此处是错误的}
}

代码注释处解析:

注释一:我们在类名后加了<T>,这里的作用就是泛型得基本用法,相当于这个T就代表一个类,但具体是哪个类我,还需要在创建这个类对象的时候,我们指定哪个类,才会知道。

注释二:这里我们创建对象时(new一个对象时)我们同样在类名后加了<Integer>,这就是我们指定T就是Integer类,则在我们创建的类对象时 T 就是Integer。

注释三:因为我们指定T为Intege类型,则在使用setVal(1,12);方法时我们可以直接传参12,然后jJVM就会进行自动拆包了。

注释四:首先说明这里的使用是编译器是会报错的,因为我们前面已经指定T为Integer了,这是传入一个String显然是不对的。所以编译器是会报错的。要想传入String储存到数组中,我们就需要在创建一个MyArray<String>对象。


泛型的擦除机制

关于泛型的擦除机制,它是Java泛型实现的一个核心概念。在Java中,泛型的类型参数在编译期间会被替换为其边界或Object类型,这个过程被称为类型擦除。这意味着在运行时,泛型的类型参数实际上是被“擦除”了,泛型代码在运行时无需知晓具体的类型参数。例如,如果有一个泛型类`List<T>`,在运行时,无论`T`是什么类型,`List<T>`都会被当作`List<Object>`来处理。

这种机制的主要目的是为了向后兼容Java的旧版本,同时减少代码重复,使得代码更加简洁。但是,这也带来了一些限制和挑战,比如不能在运行时获取泛型参数的具体类型,泛型数组的创建受到限制等。

尽管如此,通过一些技巧和设计模式,可以在一定程度上绕过这些限制,让代码更加灵活和可扩展。

类型擦除机制也意味着,在编译过程中,所有的泛型类型参数`T`都会被替换为`Object`,这就是我们通常所说的泛型擦除。由于被编译器当作`Object`类型处理,我们可以通过反射set任意类型的参数。但是,这种擦除也导致了一些问题,比如在泛型类中不能直接调用泛型参数的具体方法,因为这在编译时是未知的。解决这个问题的一种方法是给泛型参数一个边界,这样编译器就能知道泛型参数至少具有哪些方法。

总结:泛型的擦除机制是Java泛型实现的关键部分,它允许泛型代码在运行时以一种类型安全的方式处理不确定的类型,但同时也带来了一些限制和挑战。


泛型的上界

什么是泛型上界呢?

泛型上界(Bounded Type Parameters)是泛型编程中的一个概念,它允许我们为泛型类型参数指定一个边界,即限制泛型参数必须是某个类或接口的子类或实现。这样做可以提供更多的类型安全,并允许在泛型代码中使用更具体的操作。

在定义泛型类时,有时需要对传入的类型变量做一定的约束,可以通过类型边界来约束。 就如上面所说给泛型一个边界,这样编译器就能知道泛型参数至少具有哪些方法。

代码语法:

class 类名 <形参类型 extends 另一个类>{
//代码
}

例如:

public class Myarr< E extends Number >{
//代码
}

代码解析:

这里当我们要创建Myarr这个类对象时,指定 E 的类型时,那么这时我们指定的类只能是Number类或它的子类作为类型实参。如果不是那么编译器就会报错。

扩展:那么我们如果我们没有定义上界,而是public class Myarr< E >时,我们就可以看做E extends Object;

通配符上界

我们上面说过当我们使用泛型类时吗,我们需要指定一个类作为泛型参数类。那么这时我们就会产生两个场景。

场景一:

那么如果我们有一个方法,是获取各个泛型类对象里的元素,这时我们定义这个方法时的形参类型到底怎么确定呢?如果我们指定一个类就会使得形参类型定死,无法实现获取各种泛型类里面的元素。

场景二:

如果我们现在要创建一个泛型类,但是暂时还不想示例化,只是定义一个null的泛型类,后面才随机实例化范围内的泛型类对象。

为了实现上面两个场景,就有了通配符上界

class Food {public void show(){System.out.println("食物");}
}
class Fruit extends Food {public void show(){System.out.println("水果");}}
class Apple extends Fruit {public void show(){System.out.println("苹果");}
}
class Banana extends Fruit {public void show(){System.out.println("香蕉");}
}class Message<T> { // 设置泛型private T message ;public T getMessage() {return message;}public void setMessage(T message) {this.message = message;}
}

如上当我们有以上的类时。

我们实现一下场景一:

class TestDemo {public static void main(String[] args) {Message<? extends Fruit> message;//这时还不知道示例化苹果还是香蕉 message== new Message<Apple>() ;//过一会知道了,这时才指定类型,当然也可以是其它情况message.setMessage(new Apple());}}

如上:我们在想创建对象时还没知到message对象中 T 为什么类(对象),那么这时我们就使用 通配符上界Message<? extends Fruit>进行暂时限定泛型类的范围。到了后面我们知道了,这里的知道可以是 if 判断得出,或是返回值判断知道,并不像代码中的一样是我们后面主观指定了Apple。

场景二:

class TestDemo {public static void main(String[] args) {Message<Apple> message = new Message<>() ;message.setMessage(new Apple());fun(message);Message<Banana> message2 = new Message<>() ;message2.setMessage(new Banana());fun(message2);}// 此时使用通配符"?"描述的是它可以接收任意类型,但是由于不确定类型,所以无法修改public static void fun(Message<? extends Fruit> temp){
//temp.setMessage(new Banana()); //仍然无法修改!
//temp.setMessage(new Apple()); //仍然无法修改!temp.getMessage().show();System.out.println(temp.getMessage());}
}

如上:我们的fun函数,我们可以根据不同的传值,就可以得出不同的对象(限定范围内的),只需要在实参里使用通配符上界的语法。

注意:因为我们这里Message的指定类型是Fruit的子类或其本身,所以这里我们可以是直接用Fruit来直接 接受这个指定的类型的,顶多也就是向上转型,但是我们是无法对temp进行设置类的,因为这时我们传入实参的时候已经确定了的,T已经确定了一个类,而我们这时也就无法对其进行设置。

简单来说: 通配符的上界,不能进行写入数据,只能进行读取数据。 这与通配符下界是完全相反的。下文还会给大家介绍通配符下界。

public static void fun(Message<? extends Fruit> temp){
//temp.setMessage(new Banana()); //仍然无法修改!
//temp.setMessage(new Apple()); //仍然无法修改!Fruit b = temp.getMessage();System.out.println(b);
}


泛型上界与通配符上界的区别

在Java中,泛型上界和通配符上界是两个不同的概念,它们在泛型编程中扮演着不同的角色。下面分别解释它们的含义和区别:

泛型上界:

泛型上界是在声明泛型类型时指定的,用来限制泛型类型参数的类型范围。

它通常用在泛型类的声明中,例如 class Box<T extends Number> 表示 T 必须是 Number 或其子类的类型。

泛型上界是静态的,即在编译时就已经确定的。

通配符上界:

通配符上界是在实例化泛型类或使用泛型方法时使用的,用来指定通配符的类型范围。

它通常用在泛型的实例化和传递参数时,例如 List<? extends Number> 表示这个列表可以包含 Number 类型及其所有子类型的元素。

通配符上界是动态的,即在运行时可以确定具体类型。

具体区别

  • 使用场景不同
    • 泛型上界是在定义泛型类或接口时使用的,用来限制类型参数的类型。
    • 通配符上界是在实例化泛型类或调用泛型方法时使用的,用来指定具体的类型范围。
  • 类型安全
    • 泛型上界提供了编译时的类型安全检查,确保类型参数不会超出指定的范围。
    • 通配符上界则提供了运行时的类型安全,允许在运行时确定具体的类型。
  • 协变与逆变
    • 泛型上界可以是协变的(extends),也可以是逆变的(super),这取决于泛型参数是用作输入还是输出。
    • 通配符上界通常是协变的,表示可以接收更具体的类型。
  • 类型擦除
    • 泛型上界在编译时会进行类型擦除,泛型类型参数会被替换为其上界。
    • 通配符上界在运行时不会进行类型擦除,它们用于保持泛型的灵活性。
  • 实例化
    • 泛型上界在定义泛型类时实例化,不需要显式指定。
    • 通配符上界在创建泛型实例时显式指定。

通俗来说:泛型上界是在定义泛型时用来限制类型参数的,而通配符上界是在实例化泛型时用来指定具体类型范围的。

通配符下界

通配符下届与上界相似,只是指定的范围有所不一样。还有就是跟上面说的与通配符的性质是相反的。

通配符的下界,只能进行写入数据,不能进行读取数据。

代码还是类似举例:

class Food {public void show(){System.out.println("食物");}
}
class Fruit extends Food {public void show(){System.out.println("水果");}}
class Apple extends Fruit {public void show(){System.out.println("苹果");}
}
class Banana extends Fruit {public void show(){System.out.println("香蕉");}
}class Plate<T> {private T plate ;public T getPlate() {return plate;}public void setPlate(T plate) {this.plate = plate;}
}

class TestDemo {public static void main(String[] args) {Plate<Fruit> plate1 = new Plate<>();plate1.setPlate(new Fruit());通配符的下界,不能进行读取数据,只能写入数据。fun(plate1);Plate<Food> plate2 = new Plate<>();plate2.setPlate(new Food());fun(plate2);}public static void fun(Plate<? super Fruit> temp) {
// 此时可以修改!!添加的是Fruit 或者Fruit的子类temp.setPlate(new Apple());//这个是Fruit的子类temp.setPlate(new Fruit());//这个是Fruit的本身
//Fruit fruit = temp.getPlate(); 不能接收,这里无法确定是哪个父类System.out.println(temp.getPlate());//只能直接输出}
}

无法接受的原因:因为在实参传入进去时T也是被确定为Fruit的父类或Fruit类,无法确定返回的是哪个父类,所以我们无法接收。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com