《Java常见问题解法》第一章 基础知识

Java常见问题的简单解法

提示:总结于LeetCode书籍《Java常见问题的简单解法》,全书其实就是用Stream去通过函数式编程,更加简洁,快速,高效的解决实际问题。



第一章 基础知识


一、lambda表达式

1.函数式接口

函数式接口是一种包含单一抽象方法的接口。可以通过顶级类,内部类,匿名内部类来完成。

匿名内部类实现:

以Runnable接口为例,该接口包含的单一抽象方法时run,他不传入任何参数并返回void。Thread类构造函数传入Runnable作为参数。

Runnable接口源码:

@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface {@code Runnable} is used
     * to create a thread, starting the thread causes the object's
     * {@code run} method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method {@code run} is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

Thread参数为Runnable的构造函数源码:

public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }

代码示例:

public class RunnableDemo {
    public static void main(String[] args) {
        new Thread(new Runnable() {  ➊
            @Override
            public void run() {
                System.out.println(
                    "inside runnable using an anonymous inner class");
            }
        }).start();
    }
}

使用lambda表达式优化后代码示例:

new Thread(() -> System.out.println(
    "inside Thread constructor using lambda")).start();

lambda表达式必须匹配接口中单一抽象方法签名的参数类型和返回类型,这被称为与方法签名兼容。因此,lambda表达式属于接口方法的实现,可以将其赋给接口类型的引用。如:

Runnable r = () -> System.out.println(
    "lambda expression implementing the run method");
new Thread(r).start();

FilenameFilter接口实例:

File directory = new File("./src/main/java");

String[] names = directory.list((dir, name) -> name.endsWith(".java"));  
    System.out.println(Arrays.asList(names));
}

使用lambda表达式(隐性返回):

File directory = new File("./src/main/java");

String[] names = directory.list((File dir, String name) ->  ➊
    name.endsWith(".java"));

如果lambda表达式的实现多于一行,则需要使用大括号和显式返回语句:

File directory = new File("./src/main/java");

String[] names = directory.list((File dir, String name) -> {return name.endsWith(".java");
});
System.out.println(Arrays.asList(names));

二、方法引用

如果说lambda表达式本质上是将方法作为对象进行处理,那么方法引用就是将现有方法作为lambda表达式进行处理。

lambda表达式:

Stream.of(3, 1, 4, 1, 5, 9)
        .forEach(x -> System.out.println(x)); 

方法引用:

Stream.of(3, 1, 4, 1, 5, 9)
        .forEach(System.out::println);

将方法引用赋给函数式接口:

Consumer<Integer> printer = System.out::println;  ➌
Stream.of(3, 1, 4, 1, 5, 9)
        .forEach(printer);

方法引用包括三种形式

object::instanceMethod

引用特定对象的实例方法,如 System.out::println。

Class::staticMethod

引用静态方法,如 Math::max。

Class::instanceMethod

调用特定类型的任意对象的实例方法,如 String::length。

如果通过类名引用一个传入过个参数的方法,则上下文提供的第一个元素将作为方法的目标,其他元素作为方法的参数。
调用多个参数实例方法栗子:其中第一个参数s1作为了目标参数,而s2作为了方法的参数。

List<String> strings =
    Arrays.asList("this", "is", "a", "list", "of", "strings");
List<String> sorted = strings.stream()
        .sorted((s1, s2) -> s1.compareTo(s2))  
        .collect(Collectors.toList());

List<String> sorted = strings.stream()
        .sorted(String::compareTo)             
        .collect(Collectors.toList());

再举一个栗子
String::length 通过类名访问实例方法
System.out::println 通过对象引用访问实例方法

Stream.of("this", "is", "a", "stream", "of", "strings")
        .map(String::length)            
        .forEach(System.out::println);  

对应的lambda表达式:

Stream.of("this", "is", "a", "stream", "of", "strings")
        .map(s -> s.length())
        .forEach(x -> System.out.println(x));

三、构造函数引用

给定一个字符串集合,通过lambda表达式或构造函数引用,可以将其中的每个字符串映射到Person类:

List<String> names =
    Arrays.asList("Grace Hopper", "Barbara Liskov", "Ada Lovelace",
        "Karen Spärck Jones");

//lambda表达式来调整构造函数
List<Person> people = names.stream()
    .map(name -> new Person(name)).collect(Collectors.toList());

// 或采用以下方案
//使用构造函数引用来实例化Person
List<Person> people = names.stream()
    .map(Person::new).collect(Collectors.toList());

1.复制构造函数

//befor 和 after  为同一个引用,改变after的值后,before的值也相应改变
Person before = new Person("Grace Hopper");
List<Person> people = Stream.of(before)
   .collect(Collectors.toList());
Person after = people.get(0);

复制构造函数栗子:

//Person的复制构造函数,没有该构造函数使用Person::new返回的对象为Lsit<Object>
public Person(Person p) {
   this.name = p.name;
}
//此时before和after为俩个对象,不同的引用
Person before = new Person("Grace Hopper");
List<Person> people = Stream.of(before).map(Person::new)
   .collect(Collectors.toList());
Person after = people.get(0);

2.可变参数构造函数

可变参数构造器:

public Person(String... names) {
   this.name = Arrays.stream(names)
                     .collect(Collectors.joining(" "));
}

可变参数构造器的应用:

names.stream()                        
   .map(Person::new)               
   .collect(Collectors.toList());

得到的结果:
在这里插入图片描述

3.数组

构造函数引用也可以和数组一起使用。

Person[] people = names.stream()
   .map(Person::new)         
   .toArray(Person[]::new);

四、函数式接口

关键字abstract很重要,接口中的所有方法被默认为抽象方法,不需要为他们添加abstract,同时也默认为public,所以也可省略。
举个栗子:

@FunctionalInterface
public interface PalindromeChecker {
	//省略了public abstract
    boolean isPalidrome(String s);
}

注:@FunctionalInterface有俩个作用:
1.会触发编译时校验,有助于确保接口符合要求。如果接口不包含或包含多个抽象方法,程序将提示编译错误。
2.会在javadoc中生成以下语句:
Functional Interface:
This is a functional interface and can therefore be used as the assignment
target for a lambda expression or method reference.


函数式接口同样可以使用default和static方法,由于这俩种方法都有相应的实现,他们与“仅包含一个抽象方法”的要求并不矛盾,举个栗子:

该函数式接口包含了静态方法和默认方法

@FunctionalInterface
public interface MyInterface {
    int myMethod();
              
    // int myOtherMethod();  该行若不注释,则不是函数式接口

    default String sayHello() {
        return "Hello, World!";
    }
 
    static void myStaticMethod() {
        System.out.println("I'm a static method in an interface");
    }

}

接口可以继承一个或多个接口,所以若一个现有接口继承了函数式接口后,又添加了其他抽象方法,则该接口将不再是函数式接口,如:

//该接口不在是函数式接口,无法使用lambda表达式
public interface MyChildInterface extends MyInterface {
    int anotherMethod();}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值