泛型高阶晋级

定义泛型类别

如果使用泛型,只要代码在编译时没有出现警告,就不会遇到运行时ClassCastException

限制泛型的可用类型

在定义泛型类别时,预设可以使用任何的类型来实例化泛型类型中的类型,但是如果想要限制使用泛型类别时,只能用某个特定类型或者是其子类型才能实例化该类型时,可以在定义类型时,使用extends关键字指定这个类型必须是继承某个类,或者实现某个接口。
需要注意泛型无论是继承父类还是实现接口用的都是extends

package fiftyFifth;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class ListGenericFoo<T extends List> {

	private T[] fooArray;

	public T[] getFooArray() {
		return fooArray;
	}

	public void setFooArray(T[] fooArray) {
		this.fooArray = fooArray;
	}
	
	public static void main(String[] args) {
		
		ListGenericFoo<LinkedList> foo1 = new ListGenericFoo<LinkedList>();
		ListGenericFoo<ArrayList> foo2 = new ListGenericFoo<ArrayList>();
		
		LinkedList[] linkedLists = new LinkedList[10];
		
		foo1.setFooArray(linkedLists);
		
		ArrayList[] arrayList = new ArrayList[10];
		
		foo2.setFooArray(arrayList);
		//ListGenericFoo<HashMap> foo3 = new ListGenericFoo<HashMap>();
		//这样写会报错因为我们已经限定了泛型T只能是List的实现类。
		
	}
}

当没有指定泛型继承的类型或接口时,默认使用T extends Object,所以默认情况下任何类型都可以作为参数传入。

类型通配声明

public class GenericFoo<T>
{
private T foo;
public void setFoo(T foo)
{
this.foo = foo;
}
public T getFoo()
{
return foo;
}
}

GenericFoo foo1 =null;
GenericFoo foo2 =null;
那么 foo1 就只接受GenericFoo的实例,而foo2只接受GenericFoo的实例。
現在您有這麼一個需求,您希望有一個參考名称foo可以接受所有下面的实例
foo = new GenericFoo();
foo = new GenericFoo();
简单的说,实例化类型持有者时,它必須是实现List的类别或其子类别,要定义这样一个名称,您可以使用 ‘?’ 通配字元,并使用“extends”关键字限定类型持有者的型态

package fiftyFifth;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class GenericTest<T> {

	private T foo;

	public T getFoo() {
		return foo;
	}

	public void setFoo(T foo) {
		this.foo = foo;
	}
	
	public static void main(String[] args) {
		GenericTest<? extends List> genericTest = null;
		
		genericTest = new GenericTest<ArrayList>();
		genericTest = new GenericTest<LinkedList>();
		
		GenericTest<? super List> genericTest2 = null;
		//只能使用List的父类
		genericTest2 = new GenericTest<Object>();
		
		GenericTest<String> genericTest3 = new GenericTest<String>();
		genericTest3.setFoo("hello world");
		
		//GenericTest<? extends Object> genericTest4 = genericTest3;
		GenericTest<?> genericTest4 = genericTest3;
		
		System.out.println(genericTest4.getFoo());
		
		genericTest4.setFoo(null);
		
		System.out.println(genericTest4.getFoo());
		
		//genericTest4.setFoo("welcome");
		//这样写是错误的,因为这样做就失去了泛型的意义,你在实现的时候还是需要强制类型转换。
		//Object obj = "welcome";
        //genericTest4.setFoo(obj);
		//也是同样的道理。
	}
}

使用<?>或是<? extends SomeClass>的声明方式,意味著您只能通过该名称來
取得所参考实例的信息,或者是移除某些信息,但不能增加它的信息,因为只知道当中放置的是SomeClass的子类,但不确定是什么类的实例,编译器不让您加入信息, 理由是,如果可以加入信息的話,那么您就得記得取回的实例是什么类型,然后转换为原來的类型方可进行操作,这就失去了使用泛型的意义。

继承反省类别,实现泛型接口

package fiftySeventh;

public class Parent<T1,T2> {
	
	private T1 foo1;
	private T2 foo2;
	public T1 getFoo1() {
		return foo1;
	}
	public void setFoo1(T1 foo1) {
		this.foo1 = foo1;
	}
	public T2 getFoo2() {
		return foo2;
	}
	public void setFoo2(T2 foo2) {
		this.foo2 = foo2;
	}
	

}

package fiftySeventh;

public class Child<T1, T2,T3> extends Parent<T1, T2> {
	
	private T3 foo3;

	public T3 getFoo3() {
		return foo3;
	}

	public void setFoo3(T3 foo3) {
		this.foo3 = foo3;
	}
	

}

package fiftySeventh;

public interface ParentInterFace<T1, T2> {

	public void setFoo1(T1 foo1);
	public void setFoo2(T2 foo2);
	public T1 getFoo1();
	public T2 getFoo2();
}

package fiftySeventh;

public class ChildClass<T1, T2> implements ParentInterFace<T1, T2> {

	private T1 foo1;
	private T2 foo2;
	
	public void setFoo1(T1 foo1) {
		this.foo1 = foo1;
	}
	public void setFoo2(T2 foo2) {
		this.foo2 = foo2;
	}
	public T1 getFoo1() {
		return this.foo1;
	}
	public T2 getFoo2() {
		return this.foo2;
	}
	
}

For-Each循环

For-Each循环的加入简化了集合的遍历
其語法如下

package fiftySeventh;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

public class ForTest {

	public static void main(String[] args) {
		int[] arr = {1,2,3,4,5};
		
		//旧方式
		for(int i = 0; i < arr.length; i++) {
			System.out.println(arr[i]);
		}
		System.out.println("---------------");
		//新方式,增强的for循环
		for(int i:arr) {
			System.out.println(i);
		}
		System.out.println("---------------");
		String[] names = {"hello", "world", "welcome"};
		for(String name: names) {
			System.out.println(name);
		}
		System.out.println("---------------");
		int[][] arr2 = {{1,2,3}, {4,5,6}, {7,8,9}};
		for(int[] row: arr2) {
			for(int element: row) {
				System.out.println(element);
			}
		}
		System.out.println("---------------");
		Collection<String> collection = new ArrayList<String>();
		
		collection.add("one");
		collection.add("two");
		collection.add("three");
		
		for(String string : collection) {
			System.out.println(string);
		}
		System.out.println("---------------");
		List<String> list = new ArrayList<String>();
		
		list.add("a");
		list.add("b");
		list.add("c");
		for(int i=0; i <list.size(); i++) {
			System.out.println(list.get(i));
		}
		System.out.println("---------------");
		for(Iterator iterator = list.iterator(); iterator.hasNext();) {
			System.out.println(iterator.next());
		}
		System.out.println("---------------");
		for(String s: list) {
			System.out.println(s);
		}
	}
}

结果是:
1
2
3
4
5


1
2
3
4
5


hello
world
welcome


1
2
3
4
5
6
7
8
9


one
two
three


a
b
c


a
b
c


a
b
c
缺点:当遍历集合或数组时,如果需要访问集合或数组的下标,那么最好使用旧式的方式
来实现循环或遍历,而不要使用增强的 for 循环,因为它丢失了下标信息。

自动装箱/拆箱(Autoboxing/unboxing)

自动装箱/拆箱大大方便了基本类型数据和它们包装类的使用。
自动装箱: 基本类型自动转为包装类.(int >> Integer)
自动拆箱: 包装类自动转为基本类型.(Integer >> int)

package fiftySeventh;

import java.util.ArrayList;
import java.util.Collection;

public class BoxTest {

	public static void main(String[] args) {
		int a = 3;
		Collection<Integer> collection = new ArrayList<Integer>();
	    
		collection.add(new Integer(3));
		collection.add(3);//将int类型的3转换为Integer类型并放到集合当中
		collection.add(a + 3);
		
		for(Integer integer : collection) {
			System.out.println(integer);
		}
		System.out.println("-----------");
		for(int i: collection) {
			System.out.println(i);
		}
		
	}
}

结果是:
3
3
6


3
3
6

package fiftyEighth;

import java.util.HashMap;
import java.util.Map;

public class Frequency {
	
	public static void main(String[] args) {
		Map<String , Integer> map = new HashMap<String, Integer>();
		for(String string : args) {
			/*if(map.get(string) == null){
				map.put(string, 1);
			}
			else {
				int i = map.get(string);
				map.put(string, i+1);
			}*/
			Integer value = map.get(string);
//			这里不能用int自动拆箱,因为map.get()可能为空,int不可以接受null值
			map.put(string, (value == null) ? 1 : map.get(string) + 1);
		}
		
		/*for(String key: map.keySet()) {
			Integer value = map.get(key);
			System.out.println(key + "出现的次数:" + value);
		}*/
		
		System.out.println(map);
	}

}

int value = map.get(string);
最好不要这样写,因为map.get(string)可能为空,但是 int不可以接受null,Integer可以,所以应该改写为下面这样:
Integer value = map.get(string);
这样就不会出错,而且照样可以输出。

package fiftyEighth;

public class BoxTest2 {

	public static void main(String[] args) {
		Integer i1 = 128;
		Integer i2 = 128;
		Integer i3 = 127;
		Integer i4 = 127;
		Integer i5 = -128;
		Integer i6 = -128;
		Integer i7 = -129;
		Integer i8 = -129;
		if(i1 == i2) {
			System.out.println("i1 == i2");
		}
		else {
			System.out.println("i1 != i2");
		}
		
		if(i3 == i4) {
			System.out.println("i3 == i4");
		}
		else {
			System.out.println("i3 != i4");
		}
		
		if(i5 == i6) {
			System.out.println("i5 == i6");
		}
		else {
			System.out.println("i5 != i6");
		}
		
		if(i7 == i8) {
			System.out.println("i7 == i8");
		}
		else {
			System.out.println("i7 != i8");
		}
	}
}

结果是:
i1 != i2
i3 = = i4
i5 == i6
i7 != i8
为什么会这样昵?我们来看一下源码:
Returns an Integer instance representing the specified int value. If a new Integer instance is not required, this method should generally be used in preference to the constructor Integer(int), as this method is likely to yield significantly better space and time performance by caching frequently requested values. This method will always cache values in the range -128 to 127, inclusive, and may cache other values outside of this range.
Integer 类有一个缓存,它会缓存介于-128~127 之间的整数。
系统认为-128到127是我们常用的数值,所以设立了一个更好的机制。
当然如果new Integer()那么 两个对象肯定是不相等的。
其他原始数据类型和包装类类似。

可变参数(varargs)

可变参数使程序员可以声明一个接受可变数目参数的方法。 注意,可变参数必须是方法声明中的最后一个参数。

package fiftyEighth;

public class TestVarargs {
	
	private static int sum(String str,int... nums) {
		int sum = 0;
		System.out.println(str);
		for(int num : nums) {
			sum += num;
		}
		
		return sum;
	}

	public static void main(String[] args) {
		int result = sum("a", new int[] {1, 2});
		System.out.println(result);
		
		result = sum("b", 1, 2, 3);
		System.out.println(result);
	}
}


结果是:
a
3
b
6
可变参数:可变参数本质上就是一个数组,对于某个声明了可变参数的方法来说,我们既可以传递离散的值,也可以传递数组对象。但如果将方法中的参数定义为数组,那么只能传递数组对象而不能传递离散的值。

可变参数必须要作为方法参数的最后一个参数,即一个方法不可能具有两个或两个以上的可变参数。

枚举(Enums)

JDK1.5加入了一个全新类型的“类”(不是真正的类,只是很像)-枚举类型。为此JDK1.5引入了一个新关键字enum. 我们可以这样来定义一个枚举类型

public enum Color
{
Red,
White,
Blue
}

然后可以这样来使用

package fiftyNineth;

public class ColorTest {

	public static void main(String[] args) {
		Color myColor = Color.White;
		
		System.out.println(myColor);
	}
	
}

结果是:
White
枚举类型还提供了两个有用的静态方法values()和valueOf(). 我们可以很方便
地使用它们,例如
for (Color c : Color.values())
System.out.println©;


for(Color color : Color.values()) {
			System.out.println(color);
		}

结果是:
Red
White
Blue

定义枚举类型时本质上就是在定义一個类别,只不过很多细节由编译器帮您完成了,所以某些程度上, enum关键字的作用就像是class或interface。
当您使用“enum”定义 枚举类型时,实质上您定义出來的类型继承自java.lang.Enum 类型,而每个枚举的成员其实就是您定义的枚举类型的一個实例(Instance),他们都被预设为 final,所以您无法改变他们,他们也是 static 成員,所以您可以通过类型名称直接使用他们,当然最重要的,它們都是公开的(public)。

package fiftyNineth;

public enum Coin {

	penny("hello"), nickel("world"), dime("welcome"), quarter("hello world");
	
	private String value;
	
	public String getValue() {
		return value;
	}
	
	Coin(String value){
		this.value = value;
	}
	
	public static void main(String[] args) {
		Coin coin = Coin.quarter;
		System.out.println(coin.getValue());
	}
}

结果是:
hello world

public abstract class Enum<E extends Enum> extends Object implements Comparable, Serializable

This is the common base class of all Java language enumeration types. More information about enums, including descriptions of the implicitly declared methods synthesized by the compiler, can be found in section 8.9 of The Java™ Language Specification.
Note that when using an enumeration type as the type of a set or as the type of the keys in a map, specialized and efficient set and map implementations are available.
(这是所有Java语言枚举类型的公共基类。 有关枚举的更多信息,包括编译器合成的隐式声明方法的描述,可以在Java™语言规范的第8.9节中找到。
请注意,当使用枚举类型作为集合的类型或作为映射中键的类型时,可以使用专门且有效的集合和映射实现。)

枚举的比较

public final int compareTo(E o)

Compares this enum with the specified object for order. Returns a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object.

package fiftyNineth;

public class ShowEnum {
	
	public static void main(String[] args) {
		enumCompareTo(Opconstant.valueOf(args[0]));
	}
	
	public static void enumCompareTo(Opconstant constant) {
		
		System.out.println(constant);
		
		for(Opconstant c : Opconstant.values()) {
			System.out.println(constant.compareTo(c));
		}
	}
}
enum Opconstant{
	TURN_LEFT, TURN_RIGHT, SHOOT;
}

当输入SHOOT结果是:
SHOOT
2
1
0
当输入TURN_RIGHT结果是:
TURN_RIGHT
1
0
-1

枚举的顺序

public final int ordinal()

Returns the ordinal of this enumeration constant (its position in its enum declaration, where the initial constant is assigned an ordinal of zero). Most programmers will have no use for this method. It is designed for use by sophisticated enum-based data structures, such as EnumSet and EnumMap.

package fiftyNineth;

public class ShowEnum2 {

	public static void main(String[] args) {
		for(Opconstant c: Opconstant.values()) {
			System.out.printf("%d, %s, %n", c.ordinal(), c);
		}

	}

}

结果是:
0, TURN_LEFT,
1, TURN_RIGHT,
2, SHOOT,

枚举的集合EnumSet

EnumSet的名称说明了其作用,它是在J2SE 5.0后加入的新类别,可以协助您建立枚举值的集合,它提供了一系列的静态方法,可以让您指定不同的集合建立方式。

package fiftyNineth;

import java.util.EnumSet;
import java.util.Iterator;

enum FontConstant{
	Plain, Bold, Italilc, Hello;
}

public class EnumSetDemo {
	
	public static void main(String[] args) {
		EnumSet<FontConstant> enumSet = EnumSet.of(FontConstant.Plain, FontConstant.Bold);
	//最多传五个,再多传可变参数
		showEnumSet(enumSet);
		
		showEnumSet(EnumSet.complementOf(enumSet));
	
	}

	public static void showEnumSet(EnumSet<FontConstant> enumSet) {
		
		for(Iterator<FontConstant> iterator = enumSet.iterator(); iterator.hasNext();) {
			System.out.println(iterator.next());
		}
	}
}

结果是:
Plain
Bold
Italilc
Hello

public static <E extends Enum> EnumSet complementOf(EnumSet s)
Creates an enum set with the same element type as the specified enum set, initially containing all the elements of this type that are not contained in the specified set.

package fiftyNineth;

import java.util.EnumSet;
import java.util.Iterator;


public class EnumSetDemo2 {
	public static void main(String[] args) {
		EnumSet<FontConstant> enumSet = EnumSet.noneOf(FontConstant.class);
		
		enumSet.add(FontConstant.Bold);
		enumSet.add(FontConstant.Italilc);
		
		showEnumSet(enumSet);
	}

	public static void showEnumSet(EnumSet<FontConstant> enumSet) {
		for(Iterator<FontConstant> iterator = enumSet.iterator(); iterator.hasNext();) {
			System.out.println(iterator.next());
		}
	}
}

结果是:
Bold
Italilc

public static <E extends Enum> EnumSet noneOf(Class elementType)
Creates an empty enum set with the specified element type.

package sixtieth;

import java.util.ArrayList;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.List;

enum FontConstant{
	Plain, Bold, Italilc, Hello;
}

public class EnumSetDemo3 {

	public static void main(String[] args) {
		List<FontConstant> list = new ArrayList<FontConstant>();
		
		list.add(FontConstant.Bold);
		list.add(FontConstant.Italilc);
		list.add(FontConstant.Plain);
		
		showEnumSet(EnumSet.copyOf(list));
		
		
		
	}
	
	public static void showEnumSet(EnumSet<FontConstant> enumSet) {
		for(Iterator<FontConstant> iterator = enumSet.iterator(); iterator.hasNext();) {
			System.out.println(iterator.next());
		}
	}
}

结果是:
Bold
Italilc

public static <E extends Enum> EnumSet copyOf(EnumSet s)

Creates an enum set with the same element type as the specified enum set, initially containing the same elements (if any).

EnumMap

EnumMap是个专为枚举类型设计的类别,方便您使用枚举类型及Map对象.

package sixtieth;

import java.util.EnumMap;
import java.util.Map;

public class EnumMapDemo {

	public static void main(String[] args) {
		Map<Action, String> map = new EnumMap<Action, String>(Action.class);
	
		map.put(Action.TURN_LEFT, "向左转");
		map.put(Action.TURN_RIGHT, "向右转");
		map.put(Action.SHOOT, "射击");
		
		for(Action action: Action.values()) {
			System.out.println(map.get(action));
		}
	
	
	}
}

enum Action{
	TURN_LEFT, TURN_RIGHT, SHOOT;
}

结果是:
向左转
向右转
射击

与单纯的使用HashMap比较起來的差別是,在上面的程序中, EnumMap將根据枚举的順序來维护对象的排列顺序。

枚举的应用场合

实例的个数确定

package sixtieth;

public enum AccessRight {

	MANAGER, DEPARTMENT, EMPLOYEE;
}

package sixtieth;

public class AccessControl {

	public static boolean checkRight(AccessRight accessRight) {
		if(accessRight == AccessRight.MANAGER) {
			return true;
		}
		else if (accessRight == AccessRight.DEPARTMENT) {
			return false;
		}
			return false;
	}
	
	public static void main(String[] args) {
		AccessRight accessRight = AccessRight.valueOf("MANAGER");
		System.out.println(checkRight(accessRight));
	}
}

结果是:
true

静态导入

要使用静态成员(方法和变量)我们必须给出提供这个静态成员的类。使用静态导
入可以使被导入类的所有静态变量和静态方法在当前类直接可见, 使用这些静态成员无需再给出他们的类名

package sixtieth.common;

public class Common {

	public static final int AGE = 10;
	public static void  output() {
		System.out.println("hello world");
	}
}

package sixtieth;

import static sixtieth.common.Common.*;

public class StaticImportTest {

	public static void main(String[] args) {
		int a = AGE;
		
		System.out.println(a);
		
		output();
		
		
	}
	
}

结果是:
10
hello world

静态导入:
a) import static sixtieth.common.Common.AGE;
b) import static sixtieth.common.Common.output;
表示导入 Common 类中的静态成员变量 AGE 以及静态方法 output。注意:使用 import static 时,要一直导入到类中的静态成员变量或静态方法。

不过,过度使用这个特性也会一定程度上降低代码的可读性

猜你喜欢

转载自blog.csdn.net/weixin_43907332/article/details/85908226