开源软件名称:winterbe/java8-tutorial开源软件地址:https://github.com/winterbe/java8-tutorial开源编程语言:Java 92.5%开源软件介绍:Modern Java - A Guide to Java 8This article was originally posted on my blog.
Welcome to my introduction to Java 8. This tutorial guides you step by step through all new language features. Backed by short and simple code samples you'll learn how to use default interface methods, lambda expressions, method references and repeatable annotations. At the end of the article you'll be familiar with the most recent API changes like streams, functional interfaces, map extensions and the new Date API. No walls of text, just a bunch of commented code snippets. Enjoy! ★★★ Like this project? Leave a star, follow on Twitter or donate to support my work. Thanks! ★★★ Table of Contents
Default Methods for InterfacesJava 8 enables us to add non-abstract method implementations to interfaces by utilizing the Here is our first example: interface Formula {
double calculate(int a);
default double sqrt(int a) {
return Math.sqrt(a);
}
} Besides the abstract method Formula formula = new Formula() {
@Override
public double calculate(int a) {
return sqrt(a * 100);
}
};
formula.calculate(100); // 100.0
formula.sqrt(16); // 4.0 The formula is implemented as an anonymous object. The code is quite verbose: 6 lines of code for such a simple calculation of Lambda expressionsLet's start with a simple example of how to sort a list of strings in prior versions of Java: List<String> names = Arrays.asList("peter", "anna", "mike", "xenia");
Collections.sort(names, new Comparator<String>() {
@Override
public int compare(String a, String b) {
return b.compareTo(a);
}
}); The static utility method Instead of creating anonymous objects all day long, Java 8 comes with a much shorter syntax, lambda expressions: Collections.sort(names, (String a, String b) -> {
return b.compareTo(a);
}); As you can see the code is much shorter and easier to read. But it gets even shorter: Collections.sort(names, (String a, String b) -> b.compareTo(a)); For one line method bodies you can skip both the braces names.sort((a, b) -> b.compareTo(a)); List now has a Functional InterfacesHow does lambda expressions fit into Java's type system? Each lambda corresponds to a given type, specified by an interface. A so called functional interface must contain exactly one abstract method declaration. Each lambda expression of that type will be matched to this abstract method. Since default methods are not abstract you're free to add default methods to your functional interface. We can use arbitrary interfaces as lambda expressions as long as the interface only contains one abstract method. To ensure that your interface meet the requirements, you should add the Example: @FunctionalInterface
interface Converter<F, T> {
T convert(F from);
} Converter<String, Integer> converter = (from) -> Integer.valueOf(from);
Integer converted = converter.convert("123");
System.out.println(converted); // 123 Keep in mind that the code is also valid if the Method and Constructor ReferencesThe above example code can be further simplified by utilizing static method references: Converter<String, Integer> converter = Integer::valueOf;
Integer converted = converter.convert("123");
System.out.println(converted); // 123 Java 8 enables you to pass references of methods or constructors via the class Something {
String startsWith(String s) {
return String.valueOf(s.charAt(0));
}
} Something something = new Something();
Converter<String, String> converter = something::startsWith;
String converted = converter.convert("Java");
System.out.println(converted); // "J" Let's see how the class Person {
String firstName;
String lastName;
Person() {}
Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
} Next we specify a person factory interface to be used for creating new persons: interface PersonFactory<P extends Person> {
P create(String firstName, String lastName);
} Instead of implementing the factory manually, we glue everything together via constructor references: PersonFactory<Person> personFactory = Person::new;
Person person = personFactory.create("Peter", "Parker"); We create a reference to the Person constructor via Lambda ScopesAccessing outer scope variables from lambda expressions is very similar to anonymous objects. You can access final variables from the local outer scope as well as instance fields and static variables. Accessing local variablesWe can read final local variables from the outer scope of lambda expressions: final int num = 1;
Converter<Integer, String> stringConverter =
(from) -> String.valueOf(from + num);
stringConverter.convert(2); // 3 But different to anonymous objects the variable int num = 1;
Converter<Integer, String> stringConverter =
(from) -> String.valueOf(from + num);
stringConverter.convert(2); // 3 However int num = 1;
Converter<Integer, String> stringConverter =
(from) -> String.valueOf(from + num);
num = 3; Writing to Accessing fields and static variablesIn contrast to local variables, we have both read and write access to instance fields and static variables from within lambda expressions. This behaviour is well known from anonymous objects. class Lambda4 {
static int outerStaticNum;
int outerNum;
void testScopes() {
Converter<Integer, String> stringConverter1 = (from) -> {
outerNum = 23;
return String.valueOf(from);
};
Converter<Integer, String> stringConverter2 = (from) -> {
outerStaticNum = 72;
return String.valueOf(from);
};
}
} Accessing Default Interface MethodsRemember the formula example from the first section? Interface Default methods cannot be accessed from within lambda expressions. The following code does not compile: Formula formula = (a) -> sqrt(a * 100); Built-in Functional InterfacesThe JDK 1.8 API contains many built-in functional interfaces. Some of them are well known from older versions of Java like But the Java 8 API is also full of new functional interfaces to make your life easier. Some of those new interfaces are well known from the Google Guava library. Even if you're familiar with this library you should keep a close eye on how those interfaces are extended by some useful method extensions. PredicatesPredicates are boolean-valued functions of one argument. The interface contains various default methods for composing predicates to complex logical terms (and, or, negate) Predicate<String> predicate = (s) -> s.length() > 0;
predicate.test("foo"); // true
predicate.negate().test("foo"); // false
Predicate<Boolean> nonNull = Objects::nonNull;
Predicate<Boolean> isNull = Objects::isNull;
Predicate<String> isEmpty = String::isEmpty;
Predicate<String> isNotEmpty = isEmpty.negate(); FunctionsFunctions accept one argument and produce a result. Default methods can be used to chain multiple functions together (compose, andThen). Function<String, Integer> toInteger = Integer::valueOf;
Function<String, String> backToString = toInteger.andThen(String::valueOf);
backToString.apply("123"); // "123" SuppliersSuppliers produce a result of a given generic type. Unlike Functions, Suppliers don't accept arguments. Supplier<Person> personSupplier = Person::new;
personSupplier.get(); // new Person ConsumersConsumers represent operations to be performed on a single input argument. Consumer<Person> greeter = (p) -> System.out.println("Hello, " + p.firstName);
greeter.accept(new Person("Luke", "Skywalker")); ComparatorsComparators are well known from older versions of Java. Java 8 adds various default methods to the interface. Comparator<Person> comparator = (p1, p2) -> p1.firstName.compareTo(p2.firstName);
Person p1 = new Person("John", "Doe");
Person p2 = new Person("Alice", "Wonderland");
comparator.compare(p1, p2); // > 0
comparator.reversed().compare(p1, p2); // < 0 OptionalsOptionals are not functional interfaces, but nifty utilities to prevent Optional is a simple container for a value which may be null or non-null. Think of a method which may return a non-null result but sometimes return nothing. Instead of returning Optional<String> optional = Optional.of("bam");
optional.isPresent(); // true
optional.get(); // "bam"
optional.orElse("fallback"); // "bam"
optional.ifPresent((s) -> System.out.println(s.charAt(0))); // "b" StreamsA
Let's first look how sequential streams work. First we create a sample source in form of a list of strings: List<String> stringCollection = new ArrayList<>();
stringCollection.add("ddd2");
stringCollection.add("aaa2");
stringCollection.add("bbb1");
stringCollection.add("aaa1");
stringCollection.add("bbb3");
stringCollection.add("ccc");
stringCollection.add("bbb2");
stringCollection.add("ddd1"); Collections in Java 8 are extended so you can simply create streams either by calling FilterFilter accepts a predicate to filter all elements of the stream. This operation is intermediate which enables us to call another stream operation ( stringCollection
.stream()
.filter((s) -> s.startsWith("a"))
.forEach(System.out::println);
// "aaa2", "aaa1" SortedSorted is an intermediate operation which returns a sorted view of the stream. The elements are sorted in natural order unless you pass a custom stringCollection
.stream()
.sorted()
.filter((s) -> s.startsWith("a"))
.forEach(System.out::println);
// "aaa1", "aaa2" Keep in mind that System.out.println(stringCollection);
// ddd2, aaa2, bbb1, aaa1, bbb3, ccc, bbb2, ddd1 MapThe intermediate operation stringCollection
.stream()
.map(String::toUpperCase)
.sorted((a, b) -> b.compareTo(a))
.forEach(System.out::println);
// "DDD2", "DDD1", "CCC", "BBB3", "BBB2", "AAA2", "AAA1" MatchVarious matching operations can be used to check whether a certain predicate matches the stream. All of those operations are terminal and return a boolean result. boolean anyStartsWithA =
stringCollection
.stream()
.anyMatch((s) -> s.startsWith("a"));
System.out.println(anyStartsWithA); // true
boolean allStartsWithA =
stringCollection
.stream()
.allMatch((s) -> s.startsWith("a"));
System.out.println(allStartsWithA); // false
boolean noneStartsWithZ =
stringCollection
.stream()
.noneMatch((s) -> s.startsWith("z"));
System.out.println(noneStartsWithZ); // true CountCount is a terminal operation returning the number of elements in the stream as a long startsWithB =
stringCollection
.stream()
.filter((s) -> s.startsWith("b"))
.count();
System.out.println(startsWithB); // 3 ReduceThis terminal operation performs a reduction on the elements of the stream with the given function. The result is an Optional<String> reduced =
stringCollection
.stream()
.sorted()
.reduce((s1, s2) -> s1 + "#" + s2);
reduced.ifPresent(System.out::println);
// "aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2" Parallel StreamsAs mentioned above streams can be either sequential or parallel. Operations on sequential streams are performed on a single thread while operations on parallel streams are performed concurrently on multiple threads. The following example demonstrates how easy it is to increase the performance by using parallel streams. First we create a large list of unique elements: int max = 1000000;
List<String> values = new ArrayList<>(max);
for (int i = 0; i < max; i++) {
UUID uuid = UUID.randomUUID();
values.add(uuid.toString());
} Now we measure the time it takes to sort a stream of this collection. Sequential Sortlong t0 = System.nanoTime();
long count = values.stream().sorted().count();
System.out.println(count);
long t1 = System.nanoTime();
long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);
System.out.println( |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论