Default Methods For Interfaces: Streams Are Mondas

Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 11

Lembda expression

 Streams are Mondas

a monad is a design pattern


 defines how functions, operations, inputs, and outputs can be used together to
build generic types,
 Define a data type, and how values of that data type are combined.
 Create functions that use the data type, and compose them together into
operations, following the rules defined in the first step.

Default Methods for Interfaces


1. interface Formula { 1. Formula formula = new Formula() {@
2. double calculate(int a); 2. Override public double calculate(int a) {
3. default double sqrt(int a) {
3. return sqrt(a * 100);
4. return Math.sqrt(a); 4. }
5. } 5. };
6. } 6. formula.calculate(100); // 100.0
7. formula.sqrt(16); // 4.0

NOTE: The formula is implemented as an


anonymous object.

Lambda expressions

1. List < String > names = Arrays.asList("peter", "anna", "mike", "xenia");


2. Collections.sort(names, new Comparator < String > () {
3. @Override
4. public int compare(String a, String b) {
5. return b.compareTo(a);
6. }
7. });

//Above with lambda expression


1. Collections.sort(names, (String a, String b) - > {
2. return b.compareTo(a);
3. });
4.
//As you can see the code is much shorter and easier to read. But it gets even shorter:
1. Collections.sort(names, (String a, String b) - > b.compareTo(a));

//For one line method bodies you can skip both the braces {} and the return keyword. But
it gets even more shorter:
Collections.sort(names, (a, b) -> b.compareTo(a));

//The java compiler is aware of the parameter types so you can skip them as well. Let's
dive deeper into how lambda expressions can be used in the wild.

Functional Interfaces
What?  added in java 8
 They are interface with only one abstract method
 Examples of such interfaces (before java8)
1. Comparator interface
 Had only one abstract method compare
 That was used to sorting list of object
 NOTE: it has two abstract methods compare and equal. How it is possible?
o Equal method is present in object class. Any object by default extends
the Object class. Usually they do implicitly but here they are doing it
explicitly. As long as method signature of equals method is same as
what you have in object class, that kind of abstract method is allowed
2. Runnable interface
 had only one method run
 is implemented by the class which wants to create a thread

 Formally you declare an interface as Functional Interface by annotation


@FunctionalInterface
 You can have default method or static methods in the Functional interface

Why we need it?


 Java 8 has lambda expression
o It is shorthand notation for expressing anonymous classes
o they can take functionality as a parameter.
 This means you can pass entire functionality in
terms of function to lambda expressions.
Because lambda can expect there will be only one functionality and then it need
not choose between various method and directly execute the only method.
Example if there are two abstract method then lambda expression will not be sure
which one to be executed and throw compilation error.

Example:
1.
2. @FunctionalInterface
3. interface Converter<F, T>{
4. T convert(F from);
5. }
6.
7. Converter <String, Integer> converter = (from)->Integer.valueOf(from);
8. Integer converted = converter.convert("123");
9. System.out.println(converted); // 123

Method and Constructor References


The above example code can be further simplified by utilizing static method references:

1. Converter <String, Integer> converter = Integer::valueOf;


2. Integer converted = converter.convert("123");
3. System.out.println(converted); // 123

Java 8 enables you to pass references of methods or constructors via the s::` keyword. The
above example shows how to reference a static method (which method?). But we can also
reference object methods:

1. class Something {
2. String startsWith(String s) {
3. return String.valueOf(s.charAt(0));
4. }
5. }
6. Something something = new Something();
7. Converter < String, String > converter = something::startsWith;
8. String converted = converter.convert("Java");
9. System.out.println(converted); // "J"

Let's see how the `::` keyword works for constructors. First we define an example bean with
different constructors:
1. class Person {
2. String firstName;
3. String lastName;
4. Person() {}
5. Person(String firstName, String lastName) {
6. this.firstName = firstName;
7. this.lastName = lastName;
8. }
9. }

Next we specify a person factory interface to be used for creating new persons:

1. interface PersonFactory <P extends Person> {


2. P create(String firstName, String lastName);
3. }

Instead of implementing the factory manually, we glue everything together via constructor
references:

1. PersonFactory < Person > personFactory = Person::new;


2. Person person = personFactory.create("Peter", "Parker");

We create a reference to the Person constructor via Person::new. The Java compiler
automatically chooses the right constructor by matching the signature
of PersonFactory.create.

Lambda Scopes
1. Accessing outer scope variables from lambda expressions
o is very similar to anonymous objects.
2. You can access Final variables, instance fields and static variable from the local
outer scope

Accessing local variables

1. final int num = 1;


2. Converter<Integer, String> stringConverter = (from)-> String.valueOf(from+num);
3. stringConverter.convert(2); // 3

But different to anonymous objects the variable num does not have
to be declared final. However, num must be implicitly final for the
code to compile. The following code does not compile:

1. int num = 1;
2. Converter<Integer, String> stringConverter = (from)-> String.valueOf(from + num);
3. num = 3;

i. Accessing fields and static variables (from within lambda expressions)


 both read and write access to (same as anonymous objects.)

ii. Accessing Default Interface Methods


 Unlike anonymous objects, default Interface method does not work with lambda
expressions.
iii. Built-in Functional Interfaces
 The JDK 1.8 API contains many built-in functional interfaces like Comparator or
Runnable. Those existing interfaces are extended to enable Lambda support via
the @FunctionalInterfaceannotation.
 Java 8 API also has new functional interfaces like from Google Guava library.
Predicates
 They are boolean-valued functions of one argument.
 The interface contains various default methods for composing predicates to complex
logical terms (and, or, negate)

1. Predicate <String > predicate = (s) -> s.length() > 0;


2. predicate.test("foo"); // true
3. predicate.negate().test("foo"); // false
4. Predicate < Boolean > nonNull = Objects::nonNull;
5. Predicate < Boolean > isNull = Objects::isNull;
6. Predicate < String > isEmpty = String::isEmpty;
7. Predicate < String > isNotEmpty = isEmpty.negate();

Functions
 They accept one argument and produce a result.
 Default methods can be used to chain multiple functions together (compose,
andThen).

1. Function<String, Integer> toInteger = Integer::valueOf;


2. Function<String, String> backToString = toInteger.andThen(String::valueOf);
3. backToString.apply("123"); // "123"

Suppliers#
 They produce a result of a given generic type.
 Unlike Functions, Suppliers don't accept arguments.

1. Supplier < Person > personSupplier = Person::new;


2. personSupplier.get(); // new Person

Consumers#
 Consumers represents operations to be performed on a single input argument.

1. Consumer < Person > greeter = (p) - > System.out.println("Hello, " + p.firstName);
2. greeter.accept(new Person("Luke", "Skywalker"));

Comparators#
 Java 8 adds various default methods to the interface.

1. Comparator < Person > comparator = (p1, p2)->p1.firstName.compareTo(p2.firstName);


2. Person p1 = new Person("John", "Doe");
3. Person p2 = new Person("Alice", "Wonderland");
4. comparator.compare(p1, p2); // > 0
5. comparator.reversed().compare(p1, p2); // < 0

Optionals#
 It is a nifty utility to prevent NullPointerException. (not functional interfaces)
How it works?.
 a simple container for a value which may be null or non-null.
o Think of a method which may return a non-null result but sometimes return
nothing. Instead of returning null you return an Optional in Java 8.

1. Optional < String > optional = Optional.of("bam");


2. optional.isPresent(); // true
3. optional.get(); // "bam"
4. optional.orElse("fallback"); // "bam"
5. optional.ifPresent((s) -> System.out.println(s.charAt(0))); // "b"
Streams#
 They represents a sequence of elements Operation are of two type
 on them more than one operations can be 1. intermediate
performed. a) return the stream itself
Type of stream operations b) intermediate operations will
 sequential or parallel. only be executed when a
Create streams either by terminal operation is present.
 Collection.stream() or Collection.parallelStream(). 2. terminal
a) return a result of a certain
type

Example of creating a sample (stream)source in form of a list of strings:


1. List<String> stringCollection = new ArrayList<> ();
2. stringCollection.add("ddd2"); //also add (aaa2, bbb1, aaa1, bbb3,ccc, bbb2, ddd1)

1. Filter accepts a predicate to filter all stringCollection


elements of the stream. .stream()
o This operation is intermediate .filter((s) -> s.startsWith("a"))
2. ForEach is a terminal operation. It's void .map(String::toUpperCase)
3. Sorted an intermediate operation which .forEach(System.out::println);
returns a sorted view of the stream. // "AAA2", "AAA1"

// NOTE: Sorted create a sorted view of the stream without manipulating the ordering of
the backed collection.
o
4. Map# boolean anyStartsWithA = stringCollection
o The intermediate operation .stream()
o converts each element into .anyMatch((s) -> s.startsWith("a"));
another object via the given System.out.println(anyStartsWithA);
function. // true
5. Match# // other example
o Various matching operations  allMatch,
using predicates.  noneMatch
o It is terminal and return a
boolean result.
6. Count# Optional<String> reduced=stringCollection
o terminal operation .stream()
o returning the number of .sorted()
elements in the stream as a .reduce((s1, s2) -> s1 + "#" + s2);
long
7. Reduce reduced.ifPresent(System.out::println);
o terminal operation //"aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2"
o performs a reduction on the
elements of the stream with
the given function.
o The result is
an Optional holding the
reduced value.

Parallel Streams#
o Operations on sequential streams are performed on a single thread
o operations on parallel streams are performed concurrent on multiple threads.

performance by using parallel streams.


First we create a large list of unique elements:
1. int max = 1000000;
2. List < String > values = new ArrayList < > (max);
3. for (int i = 0; i < max; i++) {
4. UUID uuid = UUID.randomUUID();
5. values.add(uuid.toString());
6. }
Now we measure the time it takes to sort a stream of this collection.
Sequential Sort#
1. long t0 = System.nanoTime();
2. long count = values.stream().sorted().count();
3. System.out.println(count);
4. long t1 = System.nanoTime();
5. long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);
6. System.out.println(String.format("sequential sort took: %d ms", millis)); // sequential sor
t took: 899 ms
Parallel Sort#
1. long t0 = System.nanoTime();
2. long count = values.parallelStream().sorted().count();
3. System.out.println(count);
4. long t1 = System.nanoTime();
5. long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);
6. System.out.println(String.format("parallel sort took: %d ms", millis)); // parallel sort to
ok: 472 ms
As you can see both code snippets are almost identical but the parallel sort is roughly 50%
faster. All you have to do is change stream() to parallelStream().
Map#
they don't support streams. Instead maps now support various new and useful methods for
doing common tasks.
Map<Integer, String> map = new map.computeIfPresent(3, (num, val) - > val + num);
HashMap<>(); map.get(3); // val33
for (int i = 0; i < 10; i++) { map.computeIfPresent(9, (num, val) - > null);
map.putIfAbsent(i, "val" + i); map.containsKey(9); // false
} map.computeIfAbsent(23, num - > "val" + num);
map.forEach((id, val) -> map.containsKey(23); // true
System.out.println(val)); map.computeIfAbsent(3, num - > "bam");
map.get(3); // val33

Stream operations
 Behavior same as a functional.
 Operation must be
1. non-interfering
o it does not modify the underlying data source of the stream.(adding/
removing or updating elements)
2. stateless
o A function is stateless when the execution of the operation is deterministic,
 no lambda expression depends on any mutable variables or states
from the outer scope which might change during execution.

Stream.of() to create a stream from a bunch of object references.


 We don't have to create collections in order to work with streams
Arrays.asList("a1", "a2", "a3") Stream.of("a1", "a2", "a3")
.stream() .findFirst()
.findFirst() .ifPresent(System.out::println); // a1
.ifPresent(System.out::println); // a1
Reusing Streams
streams cannot be reused. Stream<String> stream = Stream.of("d2", "a2", "b1", "b3", "c")
As soon as you call any .filter(s -> s.startsWith("a"));
terminal operation the stream.anyMatch(s -> true); // ok
stream is closed: stream.noneMatch(s -> true); // exception
Each call to get() constructs Supplier<Stream<String>> streamSupplier =
a new stream on which we () -> Stream.of("d2", "a2", "b1", "b3", "c")
are save to call the desired .filter(s -> s.startsWith("a"));
terminal operation streamSupplier.get().anyMatch(s -> true); // ok
streamSupplier.get().noneMatch(s -> true); // ok

Advanced Operations on streams


class Person { Collect
String name;  terminal operation
int age;  transform the elements of the stream
Person(String name, int age) { into a different kind of result
this.name = name;  Collect accepts a Collector which
this.age = age; consists of four different operations: a
} supplier, an accumulator, a combiner
and a finisher.
@Override List<Person> filtered = persons
public String toString() { .stream()
return name; .filter(p -> p.name.startsWith("P"))
} .collect(Collectors.toList());
}
System.out.println(filtered); // [Peter, Pamela]
List<Person> persons = // Need a set instead of list?
Arrays.asList(  just use Collectors.toSet().
new Person("Max", 18),
new Person("Peter", 23),
new Person("Pamela", 23),
new Person("David", 12));
Double averageAge = persons Map<Integer, List<Person>> personsByAge =
.stream() persons
.collect(Collectors.averagingInt(p -> p.age)); .stream()
System.out.println(averageAge); // 19.0 .collect(Collectors.groupingBy(p -> p.age));

personsByAge
.forEach((age, p) -> System.out.format("age
%s: %s\n", age, p));

// age 18: [Max]


// age 23: [Peter, Pamela]
// age 12: [David]
IntSummaryStatistics ageSummary = persons String phrase = persons
.stream() .stream()
.collect(Collectors.summarizingInt(p -> .filter(p -> p.age >= 18)
p.age)); .map(p -> p.name)
.collect(Collectors.joining(" and ", "In
System.out.println(ageSummary); Germany ", " are of legal age."));
System.out.println(phrase);
// IntSummaryStatistics{count=4, sum=76, // In Germany Max and Peter and Pamela are
min=12, average=19.000000, max=23} of legal age.
Map<Integer, String> map = persons
.stream()
.collect(Collectors.toMap(
p -> p.age,
p -> p.name,
(name1, name2) -> name1 + ";" +
name2));

System.out.println(map);
// {18=Max, 23=Peter;Pamela, 12=David}

Now that we know some of the most powerful built-in collectors, let's try to
build our own special collector. We want to transform all persons of the
stream into a single string consisting of all names in upper letters separated
by the | pipe character. In order to achieve this we create a new collector
via Collector.of() . We have to pass the four ingredients of a collector:
a supplier, an accumulator, a combiner and a finisher.
Collector<Person, StringJoiner, String> personNameCollector =
Collector.of(
() -> new StringJoiner(" | "), // supplier
(j, p) -> j.add(p.name.toUpperCase()), // accumulator
(j1, j2) -> j1.merge(j2), // combiner
StringJoiner::toString); // finisher

String names = persons


.stream()
.collect(personNameCollector);

System.out.println(names); // MAX | PETER | PAMELA | DAVID

Since strings in Java are immutable, we need a helper class


like StringJoiner to let the collector construct our string. The supplier
initially constructs such a StringJoiner with the appropriate delimiter. The
accumulator is used to add each persons upper-cased name to the
StringJoiner. The combiner knows how to merge two StringJoiners into
one. In the last step the finisher constructs the desired String from the
StringJoiner.
Let’s try to make sense of SPRING framework
How to add library folder in build path?
Go to properties >java build path > add jars

Topic: Inversion of control


Let’s start with learning the scenario 
We have a insurance company. Company has only one user and it ask for showStatus.
Date API#
Java 8 contains a brand new date and time API under the package java.time . The new Date API is comparable with the Joda-Time library, however it's not the same. The following examples cover the
most important parts of this new API.

Clock#
Clock provides access to the current date and time. Clocks are aware of a timezone and may be used instead of System.currentTimeMillis() to retrieve the current milliseconds. Such an

instantaneous point on the time-line is also represented by the class Instant . Instants can be used to create legacy java.util.Date objects.
Clock clock = Clock.systemDefaultZone();
long millis = clock.millis();

Instant instant = clock.instant();


Date legacyDate = Date.from(instant); // legacy java.util.Date

Timezones#
Timezones are represented by a ZoneId . They can easily be accessed via static factory methods. Timezones define the offsets which are important to convert between instants and local dates and
times.
System.out.println(ZoneId.getAvailableZoneIds());
// prints all available timezone ids

ZoneId zone1 = ZoneId.of("Europe/Berlin");


ZoneId zone2 = ZoneId.of("Brazil/East");
System.out.println(zone1.getRules());
System.out.println(zone2.getRules());

// ZoneRules[currentStandardOffset=+01:00]
// ZoneRules[currentStandardOffset=-03:00]

LocalTime#
LocalTime represents a time without a timezone, e.g. 10pm or 17:30:15. The following example creates two local times for the timezones defined above. Then we compare both times and calculate the
difference in hours and minutes between both times.
LocalTime now1 = LocalTime.now(zone1);
LocalTime now2 = LocalTime.now(zone2);

System.out.println(now1.isBefore(now2)); // false

long hoursBetween = ChronoUnit.HOURS.between(now1, now2);


long minutesBetween = ChronoUnit.MINUTES.between(now1, now2);

System.out.println(hoursBetween); // -3
System.out.println(minutesBetween); // -239
LocalTime comes with various factory method to simplify the creation of new instances, including parsing of time strings.
LocalTime late = LocalTime.of(23, 59, 59);
System.out.println(late); // 23:59:59

DateTimeFormatter germanFormatter =
DateTimeFormatter
.ofLocalizedTime(FormatStyle.SHORT)
.withLocale(Locale.GERMAN);

LocalTime leetTime = LocalTime.parse("13:37", germanFormatter);


System.out.println(leetTime); // 13:37

LocalDate#
LocalDate represents a distinct date, e.g. 2014-03-11. It's immutable and works exactly analog to LocalTime. The sample demonstrates how to calculate new dates by adding or substracting days, months
or years. Keep in mind that each manipulation returns a new instance.
LocalDate today = LocalDate.now();
LocalDate tomorrow = today.plus(1, ChronoUnit.DAYS);
LocalDate yesterday = tomorrow.minusDays(2);

LocalDate independenceDay = LocalDate.of(2014, Month.JULY, 4);


DayOfWeek dayOfWeek = independenceDay.getDayOfWeek();
System.out.println(dayOfWeek); // FRIDAY
Parsing a LocalDate from a string is just as simple as parsing a LocalTime:

1. DateTimeFormatter germanFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(Locale.GERMAN);

2. LocalDate xmas = LocalDate.parse("24.12.2014", germanFormatter);

3. System.out.println(xmas); // 2014-12-24

LocalDateTime#
LocalDateTime represents a date-time. It combines date and time as seen in the above sections into one instance. LocalDateTime is immutable and works similar to LocalTime and LocalDate. We can
utilize methods for retrieving certain fields from a date-time:

1. LocalDateTime sylvester = LocalDateTime.of(2014, Month.DECEMBER, 31, 23, 59, 59);

2. DayOfWeek dayOfWeek = sylvester.getDayOfWeek();

3. System.out.println(dayOfWeek); // WEDNESDAY

4. Month month = sylvester.getMonth();

5. System.out.println(month); // DECEMBER

6. long minuteOfDay = sylvester.getLong(ChronoField.MINUTE_OF_DAY);

7. System.out.println(minuteOfDay); // 1439
With the additional information of a timezone it can be converted to an instant. Instants can easily be converted to legacy dates of type java.util.Date .

1. Instant instant = sylvester.atZone(ZoneId.systemDefault()).toInstant();

2. Date legacyDate = Date.from(instant);

3. System.out.println(legacyDate); // Wed Dec 31 23:59:59 CET 2014


Formatting date-times works just like formatting dates or times. Instead of using pre-defined formats we can create formatters from custom patterns.

1. DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMM dd, yyyy - HH:mm");

2. LocalDateTime parsed = LocalDateTime.parse("Nov 03, 2014 - 07:13", formatter);

3. String string = formatter.format(parsed);


System.out.println(string); // Nov 03, 2014 - 07:13
Unlike java.text.NumberFormat the new DateTimeFormatter is immutable and thread-safe.
For details on the pattern syntax read here.

Annotations#
Annotations in Java 8 are repeatable. Let's dive directly into an example to
figure that out.
First, we define a wrapper annotation which holds an array of the actual
annotations:
@interface Hints {
Hint[] value();
}
@Repeatable(Hints.class)
@interface Hint {
String value();
}
Java 8 enables us to use multiple annotations of the same type by
declaring the annotation @Repeatable .
Variant 1: Using the container annotation (old school)#
@Hints({@Hint("hint1"), @Hint("hint2")})
class Person {}
Variant 2: Using repeatable annotations (new school)#
@Hint("hint1")
@Hint("hint2")
class Person {}
Using variant 2 the java compiler implicitly sets up the @Hints annotation
under the hood. That's important for reading annotation informations via
reflection.
Hint hint = Person.class.getAnnotation(Hint.class);
System.out.println(hint); // null

Hints hints1 = Person.class.getAnnotation(Hints.class);


System.out.println(hints1.value().length); // 2

Hint[] hints2 = Person.class.getAnnotationsByType(Hint.class);


System.out.println(hints2.length); // 2
Although we never declared the @Hints annotation on the Person class, it's
still readable via getAnnotation(Hints.class) . However, the more
convenient method is getAnnotationsByType which grants direct access to
all annotated @Hint annotations.
Furthermore the usage of annotations in Java 8 is expanded to two new
targets:
@Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
@interface MyAnnotation {}

You might also like