目录
普通的类和方法,只能使用具体的类型,比如基本类型或者自定义的类,如果要应用多种类型的代码,就非常的不方便。
而从JDK1.5后,引入了泛型这个概念,泛型和函数的区别就是
函数传参传的是值,而泛型传的是类型,这样泛型就适用于许多许多类型,也就是将类型当做了参数
⚜️在写泛型语法之前,我们先思考一下,
如何实现一个类,这个类中包含一个数组成员,使得这个数组可以存放任何类型的数据,并且这个也可以根据成员方法返回数组中某个元素下标值。
简单分析一下吧
正常的数组是只能存放指定类型元素的值,但我们学过了,所有类的父类,默认都是Object类型的,那么数组可以为Object吗,下面我们来浅浅的试一下
创建一个类

注意看,我们将数组类型设置成Object后没有报错,说明是可行的
然后我们开始存放数据,并且获取下标元素的值

这里我们可以,强制类型转化一下

虽然说,这样数组任何类型也可以存放,但是感觉不太方便,而且条理比较混乱,
所以还是想让他只有一种数据类型,此时,我们就一个是考虑泛型了,
泛型存在的意义,就是指定当前的容器,然后想要什么类型的对象,让编译器去检查,把想要的类型,当做参数去传递。
下面直接上语法吧
class 泛型类名称<类型形参列表> {
//这里可以使用类型参数
}
class ClassName<T1,T2...,Tn>{
}
class 泛型类名称<类型形参列表> extends 继承类/*这里可以是类型参数*/ {
//这里可以使用类型参数
}
class ClassName<T1,T2...,Tn> extends ParentClass<T1> {
//这里可以使用部分类型参数
}
然后,现在把刚刚那个问题代码修改一下,引入泛型试试看效果
先写一个泛型类
//<T>代表当前类是泛型类
class MyArray2<T> {
public T[] array = (T[])new Object[10];
public T getPos(int pos) {
return array[pos];
}
public void setVal(int pos,T val) {
this.array[pos] = val;
}
}
下面我们在<>中指定类型,此时就只能存放这个数据类型的数据了
public static void main(String[] args) {
//<>中指定类型,此时这个类里面,只能放这个数据类型的数据
MyArray2<String> myArray = new MyArray2<String>();
myArray.setVal(0,"nb");
myArray.setVal(1,"xawl");
String s = myArray.getPos(1);
String s1 = myArray.getPos(0);
System.out.println(s);
System.out.println(s1);
}

我们比如在定义一个Integer类型的
MyArray2<Integer> myArray2 = new MyArray2<Integer>();
myArray2.setVal(0,12);
myArray2.setVal(1,13);
Integer I = myArray2.getPos(0);
System.out.println(I);

例子就写到这里
下面说一下泛型类语法
泛型类<类型实参> 变量名;//定义一个泛型类引用
new 泛型类<类型实参> (构造方法实参);//实例化一个泛型类对象
注意事项:
(1)类名后的<T>叫占位符,意思就是当前的类是泛型类

(2)不需要进行强制类型转化
(3)Java中,不可以new泛型类型的数组

(4)注意<>中必须要引用类型
否则,就会报错

(5)泛型类使用中可以省略类型实参的填写
MyArray<Integer> list = new MyArray<>();
这是因为,编译器可以推导出实例化需要的类型实参为Integer

擦除机制就是,在编译的过程中,将泛型T替换为Object
并且擦除机制就是编译时期的一种机制,运行期间没有泛型这个概念
思考这样一个例子
既然所有的T都替换为Object,那为什么这样就不可以写

下面看一下这个例子你就明白了


⚜️ 那为什么不能被转化呢?
很简单Object数组中存在很多的类型,然后此时你用Integer来转化很多种类型,那肯定是不行的,编译器从安全考虑不会让你通过的,
所以我们正确的应该是这样做
package Demo01;
import java.lang.reflect.Array;
import java.util.Arrays;
class MyArray<T> {
public T[] array;
public MyArray(Class<T> clazz, int capacity) {
array = (T[]) Array.newInstance(clazz,capacity);
}
public T[] getArray() {
return array;
}
public void setVal(int pos,T val) {
this.array[pos] = val;
}
public T getPos(int pos) {
return this.array[pos];
}
}
public class Test01 {
public static void main(String[] args) {//指定数组类型是Integer
MyArray<Integer> myArray = new MyArray<>(Integer.class,10);
myArray.setVal(0,10);
Integer[] tmp = myArray.getArray();
System.out.println(Arrays.toString(tmp));
}
}
下面看一下这个
我们写了一个泛型类,但是并没有带参数类型,而且也没报错,那么我们把这个叫做裸类型

这里说明裸类型,是为了兼容老版本的API保留机制,我们不要自己去使用裸类型
语法格式
class 泛型类名称<类型形参 extends 类型边界> {
......
}

下面思考一下,如果要写一个泛型类,找数组中的最大值,应该怎么做
首先,肯定是直接比较不了的,因为T是引用数据类型,引用类型比较要用比较器
所以,

这里也可以看到Comparable也是个泛型接口
所以泛型类可以这样写
class Alg<T extends Comparable<T>> {
public T findMaxVal(T[] array) {
T maxVal = array[0];
for (int i = 1; i < array.length; i++) {
if (array[i].compareTo(maxVal) > 0) {
maxVal = array[i];
}
}
return maxVal;
}
}
语法格式
方法限定符 <类型形参列表> 返回值类型 方法名称(形参列表) { ... }
直接上例子
在前面我们找数组中的最大值,是在泛型类中写了一个方法,来实现的

要调用这个方法,就必须要有个对象,如果不想有这个对象,那就可以把这个方法变成静态的就可以了,所以还有一种方法是
⚜️ 可以把这个方法写成静态的,然后放在普通类中
class Alg2 {
//静态方法
public static<T extends Comparable<T>> T findMaxVal(T[] array) {
T maxVal = array[0];
for (int i = 1; i < array.length; i++) {
if (array[i].compareTo(maxVal) > 0) {
maxVal = array[i];
}
}
return maxVal;
}
}

⚜️ 还有个方法是,继续写一个泛型方法,只不过这个方法也是成员方法
class Alg3 {
//泛型方法:成员方法
public <T extends Comparable<T>> T findMaxVal2(T[] array) {
T maxVal = array[0];
for (int i = 1; i < array.length; i++) {
if (array[i].compareTo(maxVal) > 0) {
maxVal = array[i];
}
}
return maxVal;
}
}
因为这个不是静态方法,所以还是要引用对象,
public static void main(String[] args) {
Alg3 alg3 = new Alg3();
Integer[] array = {100,200,24,34,55};
int val = alg3.findMaxVal2(array);
System.out.println(val);
}
通配符是用来解决泛型无法协变的问题的
协变指的是如果child是parent的子类,那么List<Child>也应该是List<Parent>的子类。但是泛型是不支持这样的父子类关系的。
这是因为
class MyArray<T> {
public T[] array = (T[]) new Object[10];
public T getPos(int pos) {
return this.array[pos];
}
public void setval(int pos,T val) {
this.array[pos] = val;
}
public T[] getArray() {
return array;
}
}
public class Test01 {
public static void main(String[] args) {
MyArray<Integer> myArray = new MyArray<>();
System.out.println(myArray);
MyArray<String> myArray2 = new MyArray<>();
System.out.println(myArray2);
}
}

🤠 可以看到<>尖括号中的内容,不参与类型的组成。
所以在泛型中List<Child>不是List<Parent>的子类
下面看一个例子
class Message<T> {
private T message ;
public T getMessage() {
return message;
}
public void setMessage(T message) {
this.message = message;
}
}
public class Test02 {
public static void main(String[] args) {
Message<String> message = new Message() ;
message.setMessage("xawl");
fun(message);
}
public static void fun(Message<String> temp){
System.out.println(temp.getMessage());
}
}
⚜️ 如果说此时泛型的类型不是String而是Integer

🤠 所以这里就希望的是可以接收所有的泛型类型,但是又不能够让用户随意修改。这样就需要使用通配符‘’?‘’来解决
public static void fun(Message<?> temp){
System.out.println(temp.getMessage());
}
所以通配符“?”表示,可以接收任意类型
在泛型中只有上界,没有下界
而在通配符?的基础上又产生了两个子通配符:
?extends类:设置通配符上界
?super类:设置通配符下界
语法:
Vector<? extends 类型1> x = new Vector<类型2>();
类型1指定一个数据类型,那么类型2就只能是类型1或者是类型1的子类:
Vector<? extends Number> x = new Vector<Integer>();
下面看一个例子
class Food {}
class Fruit extends Food {}
class Apple extends Fruit {}
class Banana extends Fruit {}
class Test<T> { // 设置泛型上限
private T val ;
public T getVal() {
return val;
}
public void setVal(T Val) {
this.val = val;
}
}
public class Test03 {
public static void main(String[] args) {
Test<Apple> test = new Test<>() ;
test.setVal(new Apple());
fun(test);
Test<Banana> test2 = new Test<>() ;
test2.setVal(new Banana());
fun(test2);
}
//只要是Fruit或者Fruit的子类即可
public static void fun(Test<? extends Fruit> temp){
System.out.println(temp.getVal());
}
}
⚜️ 如果此时在fun函数中对temp添加元素,就会报错
这是因为temp接收的是Fruit和他的子类,此时存储的元素应该是哪个子类无法确定,所以添加元素就会报错,所以只能获取元素
总结:通配符的上界,不能进行写入数据,只能进行读取数据。
语法:
Vector<? super 类型1> x = new Vector<类型2>();
类型1指定一个数据类型,那么类型2就只能是类型1或者是类型1的父类:
Vector<? super Integer> x = new Vector<Number>();
下面看一个例子:
class Food {}
class Fruit extends Food {}
class Apple extends Fruit {}
class Banana extends Fruit {}
class Test<T> { // 设置泛型上限
private T val ;
public T getVal() {
return val;
}
public void setVal(T Val) {
this.val = val;
}
}
public class Test03 {
public static void main(String[] args) {
Test<Fruit> test = new Test<>() ;
test.setVal(new Fruit());
fun2(test);
Test<Food> test2 = new Test<>() ;
test2.setVal(new Food());
fun2(test2);
}
public static void fun2(Test<? super Fruit> temp){
System.out.println(temp.getVal());
}
⚜️ 如果此时在fun2函数中对temp添加元素,还会报错吗
public static void fun2(Test<? super Fruit> temp){
// 此时可以修改!!添加的是Fruit 或者Fruit的子类
temp.setVal(new Apple());//这个是Fruit的子类
temp.setVal(new Fruit());//这个是Fruit的本身
// Fruit fruit = temp.getMessage(); 不能接收,这里无法确定是哪个父类
System.out.println(temp.getVal());//只能直接输出
}
总结:通配符的下界,不能进行读取数据,只能进行写入数据。
在java中,因为基本类型不是继承Object,为了在泛型代码中可以支持基本类型,java给每个基本类型都搞了一个包装类型。
| 基本数据类型 | 包装类 |
|---|---|
| byte | Byte |
| short | Short |
| int | Integer |
| long | Long |
| float | Float |
| double | Double |
| char | Character |
| boolean | Boolean |
装箱:
public static void main(String[] args) {
int a = 20;
Integer integer = a;//自动装箱
Integer integer2 = new Integer(a);//显示装箱
Integer integer3 = Integer.valueOf(a);//显示装箱
System.out.println(integer);
System.out.println(integer2);
System.out.println(integer3);
}
⚜️ 下面看一下,自动装箱的字节码文件

拆箱:
public static void main(String[] args) {
int a = 30;
Integer integer = a;
int val = integer;//自动拆箱
System.out.println(val);
int val2 = integer.intValue();//显示拆箱
System.out.println(val2);
double val3 = integer.doubleValue();//显示拆箱
System.out.println(val3);
}
⚜️ 下面看一下,自动拆箱的字节码文件

我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/
我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www
我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我
什么是ruby的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht
这篇文章是继上一篇文章“Observability:从零开始创建Java微服务并监控它(一)”的续篇。在上一篇文章中,我们讲述了如何创建一个Javaweb应用,并使用Filebeat来收集应用所生成的日志。在今天的文章中,我来详述如何收集应用的指标,使用APM来监控应用并监督web服务的在线情况。源码可以在地址 https://github.com/liu-xiao-guo/java_observability 进行下载。摄入指标指标被视为可以随时更改的时间点值。当前请求的数量可以改变任何毫秒。你可能有1000个请求的峰值,然后一切都回到一个请求。这也意味着这些指标可能不准确,你还想提取最小/
HashMap中为什么引入红黑树,而不是AVL树呢1.概述开始学习这个知识点之前我们需要知道,在JDK1.8以及之前,针对HashMap有什么不同。JDK1.7的时候,HashMap的底层实现是数组+链表JDK1.8的时候,HashMap的底层实现是数组+链表+红黑树我们要思考一个问题,为什么要从链表转为红黑树呢。首先先让我们了解下链表有什么不好???2.链表上述的截图其实就是链表的结构,我们来看下链表的增删改查的时间复杂度增:因为链表不是线性结构,所以每次添加的时候,只需要移动一个节点,所以可以理解为复杂度是N(1)删:算法时间复杂度跟增保持一致查:既然是非线性结构,所以查询某一个节点的时候
遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg
我基本上来自Java背景并且努力理解Ruby中的模运算。(5%3)(-5%3)(5%-3)(-5%-3)Java中的上述操作产生,2个-22个-2但在Ruby中,相同的表达式会产生21个-1-2.Ruby在逻辑上有多擅长这个?模块操作在Ruby中是如何实现的?如果将同一个操作定义为一个web服务,两个服务如何匹配逻辑。 最佳答案 在Java中,模运算的结果与被除数的符号相同。在Ruby中,它与除数的符号相同。remainder()在Ruby中与被除数的符号相同。您可能还想引用modulooperation.
Java的Collections.unmodifiableList和Collections.unmodifiableMap在Ruby标准API中是否有等价物? 最佳答案 使用freeze应用程序接口(interface):Preventsfurthermodificationstoobj.ARuntimeErrorwillberaisedifmodificationisattempted.Thereisnowaytounfreezeafrozenobject.SeealsoObject#frozen?.Thismethodretur
在Java中,可以像这样从一个字符串创建一个IO流:Readerr=newStringReader("mytext");我希望能够在Ruby中做同样的事情,这样我就可以获取一个字符串并将其视为一个IO流。 最佳答案 r=StringIO.new("mytext")和here'sthedocumentation. 关于java-Java的StringReader的Ruby等价物是什么?,我们在StackOverflow上找到一个类似的问题: https://st