Java之路(八):枚举类和lambda表达式

枚举类特点

  1. 枚举类很安全,不能被实例化对象,甚至用反射都无法创建对象;
  2. 可以使用name()和oridnal()方法来返回枚举对象的实例名称和序数(从0开始);
  3. 所有的枚举类都有静态方法:values可以获取当前枚举类中的所有实例;
  4. 所有的枚举类都有静态方法:valuesOf可以将String类型的字符串转换为枚举类型的对象;
  5. 枚举常量必须最先声明,并且常量之间需要用逗号隔开,最后一个常量用分号;
  6. 枚举常量之后若使用{},则表示当前枚举类的匿名内部类;
  7. 使用switch操作枚举。
public class EnumDemo {
	public static void main(String[] args) {
		//用Integer表示星期几业务含义不明确
		Employee e = new Employee();
		e.setRestDay(WeekDay.SUN);
		
		if (e.getRestDay() == WeekDay.SAT || e.getRestDay() == WeekDay.SUN)
			System.out.println("周末休息");
		else System.out.println("周一到周五休息");
		System.out.println("-------------------------");
		System.out.println(WeekDay.SAT.name());
		System.out.println(WeekDay.SAT.ordinal());
		System.out.println("-------------------------");
		WeekDay[] days = WeekDay.values();
		for (WeekDay day : days){
			System.out.println(day);
		}
		
		System.out.println("-------------------------");
		WeekDay day = WeekDay.valueOf("SUN");
		System.out.println(day);
	}
}

在枚举类中,底层操作就是创建一个一个的对象的实例: 

public class WeekDay2 {
	public static final WeekDay2 MON= new WeekDay2();//Monday
	public static final WeekDay2 TUE= new WeekDay2();//Tuesday
	public static final WeekDay2 WED= new WeekDay2();//Wednesday
	public static final WeekDay2 THU= new WeekDay2();//Thursday
	public static final WeekDay2 FRI= new WeekDay2();//Friday
	public static final WeekDay2 SAT= new WeekDay2();//Saturday
	public static final WeekDay2 SUN= new WeekDay2();//Sunday
	
	//防止创建新的对象
	private WeekDay2(){}//实际上枚举类Enum中并没有无参数构造器,
                            //只有一个带有String和int的构造器,分别表示枚举对象的名称和序数
	
}

Lambda表达式

基本语法:参数列表->表达式

  1. 参数列表
    1. 如果没有参数,直接用()表示,()不能省略;
    2. 如果只有一个参数,并且参数写了类型,参数外面一定要加();
    3. 如果只有一个参数,并且参数不写类型,这个参数外面可以不用加();
    4. 如果有两个及以上的参数,不管书否写参数类型,都必须加();
    5. 如果参数要加修饰符或者标签,参数一定要加上完整的类型;
    public class LambdaTest1 {
    	class User {
    		public String name;
    		public int score;
    
    		@Override
    		public String toString() {
    			return "User [name=" + name + ", score=" + score + "]";
    		}
    
    		public User(String name, int score) {
    			this.name = name;
    			this.score = score;
    		}
    	}
    
    	@Test
    	public void testOldUse1() {
    		User[] us = new User[] { new User("张三", 90), new User("李四", 95),
    				new User("王五", 98) };
    		//sort
    		Arrays.sort(us, new Comparator<User>() {
    
    			@Override
    			public int compare(User o1, User o2) {
    				return Integer.compare(o1.score, o2.score);
    			}
    
    		});
    		System.out.println(Arrays.toString(us));
    	}
    
    	@Test
    	public void testNewUse1() {
    		User[] us = new User[] { new User("张三", 90), new User("李四", 95),
    				new User("王五", 98) };
    		//sort
    		//Lambda表达式
    		Arrays.sort(us, (User o1, User o2) -> {
    			return Integer.compare(o1.score, o2.score);
    		});
    		System.out.println(Arrays.toString(us));
    	}
    
    	@Test
    	public void testNewUse2() {
    		User[] us = new User[] { new User("张三", 90), new User("李四", 95),
    				new User("王五", 98) };
    		//sort
    		//Lambda表达式
    		Arrays.sort(us,
    				(User o1, User o2) -> Integer.compare(o1.score, o2.score));
    		System.out.println(Arrays.toString(us));
    	}
    
    	@Test
    	public void testNewUse3() {
    		User[] us = new User[] { new User("张三", 90), new User("李四", 95),
    				new User("王五", 98) };
    		//sort
    		//Lambda表达式
    		Arrays.sort(us, (o1, o2) -> Integer.compare(o1.score, o2.score));
    		System.out.println(Arrays.toString(us));
    	}
    
    	@Test
    	public void testOldUse2() {
    		new Thread(new Runnable() {
    
    			@Override
    			public void run() {
    				System.out.println("Hello Lambda");
    			}
    		}).start();
    	}
    
    	@Test
    	public void testOldUse3() {
    		new Thread(() -> System.out.println("Hello Lambda")).start();
    	}
    
    	@Test
    	public void testOldUse4() {
    		Runnable run = new Runnable() {
    
    			@Override
    			public void run() {
    				System.out.println("Hello Lambda!");
    			}
    		};
    		new Thread(run).start();
    	}
    	
    	@Test
    	public void testNewUse5() {
    		Runnable run = () -> System.out.println("Hello Lambda!");
    		new Thread(run).start();
    		new Thread(run).start();
    	}
    }
  2. 表达式
    1. 如果表达式只有一行,那么可以直接写,不需要加{};
    2. 如果表达式有多行,需要用代码块{}表示;
    3. 如果表达式是代码块,并且需要返回值,那么在代码块中必须返回一个返回值;
    4. 如果只有单行的情况下,并且方法需要返回值,这时不能有return,编译器会自动帮我们推导return;
public class LambdaTest2 {

	@Test
	public void test1() {
		new Thread(() -> System.out.println("Hello Lambda")).start();
	}

	public static void main(String[] args) {
		Frame f = new Frame();
		f.setSize(100, 100);
		Button btn = new Button("lambda");
		btn.addActionListener(e -> System.out.println("Lambda"));
		f.add(btn);
		f.setVisible(true);
	}

	@Test
	public void test2() {
		String[] strs = new String[] { "Lambdalalala", "Cherry", "Bubbui",
				"BBC" };

		Arrays.sort(strs, (final String s1, final String s2) -> {
			if (s1 != null && s2 != null)
				return Integer.compare(s1.length(), s2.length());
			return -1;
		});
		System.out.println(Arrays.toString(strs));
	}
}

lambda表达式中的变量

  1. 参数;
  2. 局部变量;
  3. 自由变量(既不是参数也不是局部变量)

结论:lambda表达式中的自由变量会被保存,无论什么时候执行lambda表达式,都可以直接使用。

  1. 自由变量在lambda表达式中不能被修改(在底层已经被用final修饰);(操作自由变量的代码块,称为闭包)
  2. 参数和局部变量的使用方式和普通的变量使用方式相同;
  3. lambda表达式中的this指向创建lambda表达式的方法中的this;
public class LambdaTest3 {

        /**
	 * lambda表达式中this指向创建lambda表达式的方法中的this
	 * @param content
	 * @param times
	 */
	
	public void repeatPrint(String content, int times) {
		System.out.println(this);//com.Bryan._06_lambda.LambdaTest3@514713
		Runnable run = ()->{
			System.out.println(this);//com.Bryan._06_lambda.LambdaTest3@514713
		};
		new Thread(run).start();
	}
	
	public void repeatPrint2(String content, int times) {
		Runnable run = new Runnable(){

			@Override
			public void run() {
				System.out.println(this);//com.Bryan._06_lambda.LambdaTest3$1@145df5f
			}
			
		};
		new Thread(run).start();
	}
	
	@Test
	public void testVar(){
		this.repeatPrint("Hello", 5);
	}
}

函数式接口

  1. 我们能够写lambda表达式的地方:一个接口,且里面只有一个抽象方法;
  2. 在Java中,把只有一个抽象方法的接口称为函数式接口,如果一个接口是函数式接口,我们可以在接口上添加@functionalInterface标签,表明这是一个函数式接口;(检查,文档)
     
  3. 无论是否标识@functionalInterface,只要满足函数式接口的接口,Java都会识别为函数式接口;
  4. 简化函数式接口的使用是JAVA中提供lambda表达式唯一的作用;
  5. 可以用接口直接引用一个lambda表达式;
  6. 函数式接口里可以写Object对象中的方法;
  7. lambda表达式中的异常处理:lambda表达式中产生的异常要么直接在代码块中处理,要么在接口的方法声明抛出。
public class LambdaTest4{
	
	public void wrapWork(IMyWork work){
		System.out.println("Do some wrap work...");
		try {
			work.dowork();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	@Test
	public void test() throws Exception{
		IMyWork work = () -> {
			System.out.println("haha");
			Thread.sleep(1000);
		};
		this.wrapWork(work);
	}
}
@FunctionalInterface
public interface IMyWork {

	void dowork() throws Exception;
	
	boolean equals(Object o);
}

方法引用

  1. 类::静态方法
  2. 对象::方法
  3. 对象::静态方法
public class LambdaTest5 {

	@Test
	public void test1() {
		Integer[] is = new Integer[]{2,2,8,4,1,4,7,3};
		//Arrays.sort(is,(x,y) -> Integer.compare(x, y));
		//也是lambda表达式,方法引用(类::静态方法)
		Arrays.sort(is,Integer::compare);
		System.out.println(Arrays.toString(is));	
	}
	
	public int compare(int x,int y){
		return Integer.compare(x, y);
	}
	
	@Test
	public void test2(){
		LambdaTest5 lt = new LambdaTest5();
		Integer[] is = new Integer[]{2,2,8,4,1,4,7,3};
		//方法引用(对象::方法)
		//Arrays.sort(is,lt::compare);
		//Arrays.sort(is,this::compare);
		List<Integer> list =Arrays.asList(2,2,8,4,1,4,7,3);
		//list.forEach(x->System.out.println(x));
		list.forEach(System.out::println);
		//System.out.println(Arrays.toString(is));
	}
}

构造器引用(需要一个无参构造方法)

  1. 类::new
  2. 构造器引用对应的函数式接口里面的方法格式一定是:返回一个对象/方法没有参数
public class LambdaTest5 {

	@Test
	public void test1() {
		Integer[] is = new Integer[] { 2, 2, 8, 4, 1, 4, 7, 3 };
		//Arrays.sort(is,(x,y) -> Integer.compare(x, y));
		//也是lambda表达式,方法引用(类::静态方法)
		Arrays.sort(is, Integer::compare);
		System.out.println(Arrays.toString(is));
	}

	public int compare(int x, int y) {
		return Integer.compare(x, y);
	}

	@Test
	public void test2() {
		LambdaTest5 lt = new LambdaTest5();
		Integer[] is = new Integer[] { 2, 2, 8, 4, 1, 4, 7, 3 };
		//方法引用(对象::方法)
		//Arrays.sort(is,lt::compare);
		//Arrays.sort(is,this::compare);
		List<Integer> list = Arrays.asList(2, 2, 8, 4, 1, 4, 7, 3);
		//list.forEach(x->System.out.println(x));
		list.forEach(System.out::println);
		//System.out.println(Arrays.toString(is));
	}

	public <T> List<T> asList(IMyCreator<List<T>> creator, T... a) {
		List<T> list = creator.create();
		for (T t : a)
			list.add(t);
		return list;
	}

	@Test
	public void test3() {
		//构造方法引用
		//类::new
		//List<Integer> list = Arrays.asList(2, 2, 8, 4, 1, 4, 7, 3);
		List<Integer> list = this.asList(() -> {return new ArrayList();} , 2, 2, 8, 4);
		List<Integer> list2 = this.asList(ArrayList::new , 2, 2, 8, 4);
		list.forEach(System.out::println);
		System.out.println(list.getClass());
	}
}

接口的默认方法

  1. 可以实现多继承;
  2. 解决冲突:
    1. 如果两个接口中有相同方法签名的默认方法,子类必须实现冲突的方法,指定使用某一个父接口中的默认实现;
    2. 如果父类里面有何接口中一个默认方法有相同的方法签名,那么使用父类里的方法;
    3. 永远不要期望用接口里面的默认方法来改变Object对象里面的方法;
  3. 接口里面可以直接写接口的静态方法了;
  4. 常见的一些模式被取代
    1. 工具类
    2. 适配器模式
发布了40 篇原创文章 · 获赞 17 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_42650988/article/details/87712401