【大数据开发】Java语言基础——深入泛型day15

只作了总结和怎么用,给了很多例子,讲究实践,不讲泛型是做什么用的

一、定义接口、类

public interface List<E>
{
	//在接口里,E可以作为类型使用
	//下面方法可以用E作为参数类型
	void add(E x);
	Iterator<E> iterator();
}

泛型的实质:允许在定义接口、类时声明类型形参,类型形参可以在整个接口、类体内当成类型使用
包含泛型 声明的类型可以在定义变量、创建对象时传入一个类型实参。
在接口中增加泛型声明

//泛型接口
interface Inter<E>
{
	void show(E e);
	void fun(String str);
}
class Demo implements Inter<Integer>
{
    public void show(Integer e)
	{
		System.out.println(e);
	}

	public void fun(String str){
		System.out.println(str);
	}
}
class Demo0<E> implements Inter<E>
{
	public void show(E e){
		System.out.println(e);
	}
	public void fun(String str){
		System.out.println(str);
	}
}
class Demo9 
{
	public static void main(String[] args) 
	{
		Demo0<Integer> dd =  new Demo0<>();
		dd.show("kk");
		/*
		Demo d = new Demo();
		d.show(44);
		d.fun("ok");*/
	
	}
}

在类中增加泛型声明

public class Apple<T>
{
	private T info;
	public Apple(){}
	public Apple(T info)
	{
		this.info = info;
	}
	
	public void setInfo(T info)
	{
		this.info = info;
	}
	
	public T getInfo()
	{
		return info;
	}

	public static void main(String[] args)
	{
		//T变成String
		Apple<String> ap = new Apple<>("苹果");
		System.out.println(ap.getInfo());
		//T变成Double
		Apple<Double> a = new Apple<>(16.5);
		System.out.println(a.getInfo());
		
	}
}

应该注意的是:
1.在使用泛型时,传入泛型的应当是引用数据类型,而不是普通数据类型!!
2.当创建泛型声明的自定义类,在为该类定义构造器时,构造器名还是原来的类名,不要增加泛型声明,可以查看上面代码示例。
下面给出了几个例子:

/*
泛型:使用<>来接收一种引用数据类型,作用是在编译时期就检查集合中存储的数据的类型
如果类型不一致,就会提示错误,从而把运行时期的问题转移到了编译时期,提高了程序的
安全性

泛型用于编译时期

*/
import java.util.*;
class Demo6 
{
	public static void main(String[] args) 
	{
		ArrayList<String> list = new ArrayList<String>();

		list.add("java01");
		list.add("java01");
		list.add("java01");
		//list.add(66);

		Iterator<String> ite = list.iterator();
		while(ite.hasNext())
		{
			String str=ite.next();
			//Object obj=ite.next();
			//String str=(String)obj;
            System.out.println(str.toUpperCase());
		}
	}
}
class Student
{
}
class Worker
{
}
//使用泛型解决  定义泛型类
class Tool<E>  //这就是可以使用泛型的类
{
	private E obj;

	public void setObj(E obj) //方法上使用泛型
	{
		this.obj=obj;
	}
	public E getObj()
	{
		return obj;
	}
}
class Demo7 
{
	public static void main(String[] args) 
	{
		Tool<Student> tool=new Tool<>();
		tool.setObj();
		Student w = (Student)tool.getObj();
	}
}

二、从泛型类派生子类

创建了带有泛型声明的接口、父类后,可以为该接口创建实现类,或从该父类派生子类,但是,当使用这些接口、父类时,不能再包含类型参数,如下是错误的:

public class A extends Apple<T>{}

我们已经知道,定义方法时可以添加并声明数据形参,调用方法时必须为这些数据形参传入实际的参数。类似的,在定义类、接口、方法时可以声明类型形参,因此在使用类、接口、方法时应该为类型参数传入实际的类型,如下是正确的:

public class A extends Apple<String>{}

但是,在使用类、接口时也可以不传入实际的类型参数,如下:

public class A extends Apple{}

应该注意的是:
(1)在重写父类方法时,其返回值应该和重写的方法返回值保持一致。
(2)创建带泛型声明的接口的实现类上面几乎完全一模一样,不再赘述。
(3)此外,不管泛型的实际类型参数是什么,它们在运行的时候总是有同样的类。在内存中只占用一块内存空间,因此在静态方法静态初始化块静态变量的声明中不允许使用类型形参。由于系统不会真正生成泛型类,所以instanceof运算符后不能使用泛型类(这就是所谓的并不存在真正的泛型类)
(4)如果Foo是Bar的一个子类型(子类或者子接口),而G是具有泛型声明的类或者接口,G并不是G的子类型!这一点非常值得注意。例如Collection并不是Collection的子类型!
在这里插入图片描述

三、类型通配符

1.使用类型通配符

类型通配符是一个(?),例如List<?>,表示元素类型位置的List,它的元素类型可以匹配任何类型,可以使用get()方法安全访问该集合,因为该集合的元素类型是Object类型的。然而,这种带通配符的List仅表示它是各种泛型的父类,并不能把元素加入其中,使用add()方法会引起编译错误,这是因为程序无法确定该集合的元素是什么元素类型,所以不能添加对象。唯一可以添加的例外是null,因为它是所有引用类型的实例。

2.设定类型通配符的上限

还有一种特殊的情形,我们不想使这个List<?>是任何泛型List的父类,只想表示它是某一类泛型List的父类。所以我们需要一种泛型表示方法,它可以表示所有Shape泛型List的父类。为了满足这种需求,Java泛型提供了被限制的泛型通配符。被限制的泛型通配符表示如下:

//它表示所有Shape泛型List的父类
List<? extends Shape>

此时我们可以将List 对象当成List<? extends Shape>使用,只要List后尖括号里的类型是Shape的子类型即可。这种情况下,我们称Shape为类型通配符的上限。

因为不知道这个受限制的通配符的具体类型,所以不能把Shape对象或其子类对象加入这个泛型集合中:

public void addRectangle(List<? extends Shape> shapes){
	//下面代码将引起编译错误
	shapes.add(0,new Rectangle());
}

3.设定类型形参的上限

格式

<T extends E>

表示传给该类型形参的实际类型T要么是该类型T的上限参数E,要么是该上限类型E的子类。若所传递的参数T既不是E类型,也不是E类型的子类型,则会导致编译错误。

public class Apple<T extends Number>{

	private T col;

	public static void main(String[] args){
		Apple<Integer> ai = new Apple<Integer>();
		Apple<Double> ad = new Apple<Double>();
		//下面代码将引起编译错误,因为String不是Number的子类型
		Apple<String> as = new Apple<String>();
	}
}

与类同时继承父类和实现接口类似,在为类型形参指定多个上限时,所有的接口上限必须位于类上限之后,也就是说,如果需要为类型形参指定类上限,类上限必须位于第一位

4.泛型方法

下面是定义泛型方法的规则:

(1)所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前。
(2)每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛
型类型名称的标识符。
(3)类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。
(4)泛型方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型(像int,double,char的等)。
定义泛型方法

//定义泛型方法
class Test<T>
{
	//当类上的泛型确定了,方法的参数的类型才确定
	public void show(T name)
	{
		System.out.println(name);
	}
    //方法自己使用泛型,和类没关系,也就是什么类型都行
	public <T> void fun(T t)
	{
		System.out.println(t);
	}
    //静态方法只能自己用泛型
	public static <T> void ff(T tt)
	{
		System.out.println(tt);
	}
}
class Demo8 
{
	public static void main(String[] args) 
	{
		Test<Integer> test=new Test<>();//泛型是在创建对象时确定下来的
        test.show(56);

		test.fun(66);
		test.fun("hello");

		test.ff(5.6);
	}
}

5.泛型方法和泛型通配符的区别

大多数时候可以使用泛型方法来代替类型通配符,例如

public interface Collection<E>
{
    boolean containAll(Collection<E> C);
    boolean addAll(Collection<? extends E> C);
}
//可以改写成
public interface Collection 
{
    <T> boolean containAll(Collection<T> C);
    <T> boolean addAll(Collection<T> c);
}

(1)上面方法使用了泛型形式,这时定义泛型形参时设定上限(其中E是Collection接口)里定义的泛型,在该接口里E可以当成普通类型)。上面的两个方法中泛型E只使用一次,泛型形参T产生的唯一效果是可以在不同的调用点传入不同的实际类型。对于这种情况,应该使用通配符。通配符就是被设计用来支持灵活的子变化的。
(2)泛型方法允许泛型形参被用来表示方法的一个或者多个参数之间的类型依赖关系,或者方法返回值与参数之间的类型依赖关系。如果没有这种类型依赖关系,就不应该使用泛型方法。如果某个方法中一个形参(a)的类型或者返回值的类型依赖于另一个形参(b)的类型,则形参(b)的类型声明不应该使用通配符,因为形参(a)或返回值的类型依赖于该形参(b)的类型,如果(b)的类型无法确定,程序就无法定义形参(a)的类型,在这种情况下,只能考虑使用在方法签名中声明泛型也就是泛型方法。
(3)类型通配符与泛型方法(在方法签名中显示声明泛型形参)还有一个显著的区别:类型通配符既可以在方法签名中定义形参的类型,也可以用于定义变量的类型。但是泛型方法中的泛型形参必须在对应方法中显示声明

6.例子

在静态方法中使用泛型(分别利用泛型方法和通配符)

import java.util.*;
class Demo11 
{
	public static void main(String[] args) 
	{
		ArrayList<String> list = new ArrayList<String>();

		list.add("java01");
		list.add("java02");
		list.add("java03");


		dieDai2(list);

		ArrayList<Integer> list2 = new ArrayList<Integer>();

		list2.add(66);
		list2.add(88);
		list2.add(99);

		dieDai2(list2);
	}
	//使用泛型通配符
	public static void dieDai2(Collection<?> list)
	{
		Iterator<?> ite=list.iterator();
		while(ite.hasNext())
		{
			Object obj = ite.next();
			System.out.println(obj);
		}
	}
	//使用泛型方法
    public static <E> void dieDai(Collection<E> list)
	{
		Iterator<E> ite=list.iterator();
		while(ite.hasNext())
		{
			E obj = ite.next();
			System.out.println(obj);
		}
	}

}

7.? super T和? extends T的区别和应用

转载一篇博客
总结

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值