Java函数式编程
函数式编程
从JDK1.8开始为了简化使用者进行代码的开发,专门提供有lambda表达式的支持,利用此操作形式可以实现函数式的编程,对于函数编程比较著名的语言是:haskell、Scala,利用函数式的编程可以避免掉面向对象编程过程中的一些繁琐的问题。
面向对象在其长期发展的过程中一直有一部分的反对者,这些反对者认为面向对象的设计过于复杂繁琐,以一个最简单的程序为例:
范例:观察传统开发中的问题
1 interface IMessage{ 2 public void send(String str); 3 } 4 public class Main { 5 6 public static void main(String[] args) { 7 IMessage msg=new IMessage() { 8 @Override 9 public void send(String str) { 10 System.out.println("消息发送:"+str); 11 } 12 }; 13 msg.send("hello"); 14 } 15 }
1,Lamda表达式在这个程序中,实际上核心的功能只有一行语句【System.out.println("消息发送:"+str);】,但是为了这样一行核心语句我们需要按照完整的面向对象给出的设计结构进行开发。于是这些问题随着技术的不断发展也是越来越突出。
范例:使用Lambda表达式实现上面相同的功能
1 interface IMessage{ 2 public void send(String str); 3 } 4 public class Main { 5 6 public static void main(String[] args) { 7 IMessage msg=(str)->{ 8 System.out.println("消息发送:"+str);}; 9 msg.send("hello"); 10 } 11 }
Lambda表达式如果想要使用,那么必须有一个重要的实现要求:SAM(Single Abstract Method),只有一个抽象方法,在这个接口里面只是提供一个send()方法,除此之外没有任何其他方法定义,所以这样的接口就被定义为函数式接口,而只有函数式接口才能被Lambda表达式所使用。现在整个程序代码里面会发现真的只是编写了一行语句,于是利用这种形式就避免了复杂的面向对象结构化的要求。
范例:函数式接口里面的方法只能有一个
1 @FunctionalInterface//函数式接口 2 interface IMessage{//JDK1.8之后接口里面可以定义普通方法和Static方法 3 public void send(String str); 4 public default void print() {//这是一个 5 System.out.println("公共方法"); 6 } 7 } 8 public class Main { 9 10 public static void main(String[] args) { 11 IMessage msg=(str)->{ 12 System.out.println("消息发送:"+str);}; 13 msg.send("hello"); 14 msg.print(); 15 } 16 }
①方法没有参数:()->{}对于Lambda表达式而言,需要提供有如下几种格式:
②方法有参数:(参数,参数)->{}
③如果现在只有一行语句返回:(参数,参数)->语句;
范例:定义没有参数的方法
1 @FunctionalInterface//函数式接口 2 interface IMessage{//JDK1.8之后接口里面可以定义普通方法和Static方法 3 public void send(); 4 } 5 public class Main { 6 public static void main(String[] args) { 7 IMessage msg=()->{ 8 System.out.println("消息发送:这是个无参lambda形式");}; 9 msg.send(); 10 } 11 }
范例:定义有参数的
1 @FunctionalInterface//函数式接口 2 interface IMath{//JDK1.8之后接口里面可以定义普通方法和Static方法 3 public int add(int x,int y); 4 } 5 public class Main { 6 public static void main(String[] args) { 7 IMath math=(t1,t2)->{ 8 return t1+t2; 9 }; 10 System.out.println(math.add(10,20)); 11 } 12 }
范例:简化Lambda操作以上表达式只有一行语句,这个时候也可以进一步简化。
1 @FunctionalInterface//函数式接口 2 interface IMath{//JDK1.8之后接口里面可以定义普通方法和Static方法 3 public int add(int x,int y); 4 } 5 public class Main { 6 public static void main(String[] args) { 7 IMath math=(t1,t2)->t1+t2;{ 8 }; 9 System.out.println(math.add(10,20)); 10 } 11 }
利用lambda可以摆脱传统面向对象的关于结构的限制,使得代码更加的简便。只用于替换SAM的函数式接口
2,方法引用
引用数据类型最大的特点是可以进行内存的指向处理,但是在我们传统的开发之中一直所使用的只是对象的引用操作,从JDK1.8之后也有方法的引用,即不同的方法名称可以描述同一个方法。如果进行方法的引用在java里面提供有如下的四种形式。
①引用静态方法: 类名称::static 方法名称;
②引用某个实例对象的方法: 实例化对象::普通方法;
③引用特定类型的方法: 特定类::普通方法;
④引用构造方法: 类名称::new。
在String类里面提供有String.valueOf()方法,这个方法属于静态方法。
方法定义:public static String valueOf(int i),该方法有参数,并且有返回值;
·范例:
1 @FunctionalInterface//函数式接口 2 interface IFunction<P,R>{//P描述参数,R描述返回值 3 public R change(P p); 4 } 5 public class Main { 6 public static void main(String[] args) { 7 IFunction<Integer,String>fun=String::valueOf; 8 String str=fun.change(100); 9 System.out.println(str.length()); 10 } 11 }
·范例:引用实例化对象中的方法利用方法引用这一概念可以为一个方法定义多个名字,但是要求必须是函数式接口。
在String类里面有一个转大写的方法:public String toUpperCase();
·这个方法是必须有实例化对象提供的情况下才可以调用;
1 @FunctionalInterface//函数式接口 2 interface IFunction<R>{//P描述参数,R描述返回值 3 public R upper(); 4 } 5 public class Main { 6 public static void main(String[] args) { 7 IFunction<String>fun="hello"::toUpperCase; 8 String str=fun.upper(); 9 System.out.println(str); 10 } 11 }
这是一个普通方法,如果要引用普通方法,则往往都需要实例化对象,但是如果说现在你不想给出实例化对象,只是想引用这个方法。则就可以使用特定类进行引用处理。在进行方法引用的时候也可以进行特定类的一些操作方法,在String类中提供一个字符串大写关系的比较: public int compareTo(String anotherString);
·范例:引用特定类中的方法
1 @FunctionalInterface//函数式接口 2 interface IFunction<P>{//P描述参数,R描述返回值 3 public int compare(P p1,P p2); 4 } 5 public class Main { 6 public static void main(String[] args) { 7 IFunction<String>fun=String::compareTo; 8 System.out.println(fun.compare("A","a")); 9 } 10 }
范例:引用构造方法在方法里面最具杀伤力的是构造方法的引用。
1 class Person{ 2 private String name; 3 private int age; 4 public Person(String name,int age){ 5 this.name=name; 6 this.age=age; 7 } 8 @Override 9 public String toString() { 10 return "姓名:"+this.name+",年龄:"+this.age; 11 } 12 } 13 @FunctionalInterface//函数式接口 14 interface IFunction<R>{//P描述参数,R描述返回值 15 public R create(String s,int a); 16 } 17 public class Main { 18 public static void main(String[] args) { 19 IFunction<Person>fun=Person::new; 20 System.out.println(fun.create("Wanyu",25)); 21 } 22 }
提供方法引用的概念更多的情况下也只是弥补了对于引用的支持功能。
3,内建函数式接口
在JDK1.8之中提供Lambda表达式也提供方法引用,但是你会发现现在由开发者自己定义函数式接口往往需要使用【@FunctionInterface】注解来进行大量的声明,于是很多的情况下如果为了方便则可以引用系统中提供的函数式接口。
在系统值周专门提供有有一个java.util.function的开发包,里面可以直接使用函数式接口,在这个包下面可以直接使用如下的几个核心接口:
①功能性函数式接口:
·在String类中有一个方法判断是否以指定的字符串开头:public boolean startsWith(String str)
接口定义 |
接口使用 |
@FunctionalInterface public interface Function<T,R>{ public R apply(T t); } |
import java.util.function.*; |
②消费型函数式接口:只能进行数据处理操作
·在进行系统数据输出的时候使用的是:System.out.println()
接口定义 |
接口使用 |
@FunctionalInterface public interface Consumer<T>{ public void accept(T t); } |
import java.util.function.*; |
③供给型函数式接口:
·在String类中提供有转小写方法,这个方法没有接收参数,但是有返回值;
|-方法:public String toLowerCase();
接口定义 |
接口使用 |
@FunctionalInterface public interface Supplier<T>{ public T get(); } |
import java.util.function.*; |
④断言型函数式接口:进行判断处理
·在String类有一个compareToIgnoreCase()
接口定义 |
接口使用 |
@FunctionalInterface public interface Predicate<T>{ public boolean test(T t); } |
import java.util.function.*; |
以后对于实际开发之中,如果JDK本身提供的函数式接口可以被我们所使用,那么就没有必要重新定义了。