Good Implementation of orElse()
maybe.map(x -> logic handling assuming value exists)
.orElse(logic handling if value doesn’t exist)
3 ways to sort
.sort()
Where natural ordering is present.
// If classes already have Comparable built in
List<Integer> nums = List.of(3, 1, 2); nums.stream().sorted().toList(); // works, Integer already implements Comparable
// Use lambda
events.stream()
.sorted((e1, e2) -> e1.getPriority() - e2.getPriority())
.toList();
// Pass a Comparator.comparing into it
events.stream()
.sorted(Comparator.comparing(Event::getPriority))
.toList();
Comparable
// Event.java
public class Event implements Comparable<Event> {
...
@Override
public int compareTo(Event other) {
return this.priority - other.priority;
// sorts by priority, ascending
}
...
}
// Main.java
...
List<Event> sorted = events.stream()
.sorted() // calls compareTo() internally
.toList();
...
}
Comparator
Allows us to sort objects that do not implement the Comparable interface. Comparator is an interface used to define a sorting order for objects outside of the class itself. In other words, the actual class no longer needs to implement Comparable and define a compareTo method. I just outsource it to the Comparator interface.
Comparator Interface
interface Comparator<T> {
int compare(T o1, T o2);
}
Returns:
- negative → o1 comes before o2
- zero → o1 equals o2
- positive → o1 comes after o2
Implementing the Comparator Interface by creating a class
// Sort Person by age ascending
class AgeComparator implements Comparator<Person> {
public int compare(Person p1, Person p2) {
return Integer.compare(p1.age, p2.age);
}
}
// Sort Person by name ascending
class NameComparator implements Comparator<Person> {
public int compare(Person p1, Person p2) {
return p1.name.compareTo(p2.name); // String already has compareTo
}
}
Using the class
// Sort by age
Stream.of(new Person(..), ..)
.sorted(new AgeComparator())
.toList();
// Sort by name
Stream.of(new Person(..), ..)
.sorted(new NameComparator())
.toList();
1. No parameter constructor and methods
This is possible.
class JustRide implements Service {
private static final int FARE = 22;
private static final int SURCHARGE = 500;
private static final int PEAK_START = 600;
private static final int PEAK_END = 900;
public int computeFare(int distance, int numPassengers, int timeOfService) {
return FARE * distance +
(timeOfService >= PEAK_START && timeOfService <= PEAK_END
? SURCHARGE
: 0);
}
@Override
public String toString() {
return "JustRide";
}
}
2. IS-A Relationships
// Concrete Class
class Vehicle { }
class Car extends Vehicle { }
Car IS-A Vehicle
// Interface
interface Service {
int computeFare(int distance, int passengers, int time);
}
class JustRide implements Service {
public int computeFare(int distance, int passengers, int time) {
return 0;
}
}
JustRide IS-A Service
// Interface extend Interface
interface Shape {
double area();
}
interface Shape3D extends Shape {
double volume();
}
Shape3D IS-A Shape
// Transitive Inheritance
class Animal {}
class Mammal extends Animal {}
class Dog extends Mammal {}
Dog IS-A Mammal
Dog IS-A Animal
// Assignment
Service s = new JustRide();
JustRide IS-A Service
3. HAS-A Relationships
// Instance fields
class Engine { }
class Car {
private Engine engine;
}
Car HAS-A Engine
// Composition
class Car {
private Engine engine = new Engine();
}
Car HAS-A Engine
// Provided Externally
class Car {
private Engine engine;
Car(Engine engine) {
this.engine = engine;
}
}
Car HAS-A Engine
4. You don’t need to implement Service just because your method wants to use it as a parameter
class Request {
...
int computerFare(Service service) {
}
...
}
5. Importing Streams and Lists
import java.util.List;
import java.util.stream.Stream;
6. Records automatically generate accessors
record Pair<T,U>(T t, U u) {}
p.u()
p.t()
7. Solving Cyclic Dependencies
Problem
class Driver {
Stream<String> rank() {
NormalCab cab = new NormalCab();
return cab.getServices().sorted();
}
}
class NormalCab {
Stream<String> getServices() {
Driver d = new Driver();
return Stream.of("JustRide", "TakeACab");
}
}
getServices() is implemented in NormalCab.
Driver has no idea what getServices() is.
Solving via Abstract Class
abstract class Driver {
Stream<String> rank() {
return getServices().sorted();
}
abstract Stream<String> getServices();
}
class NormalCab extends Driver {
Stream<String> getServices() {
return Stream.of("JustRide", "TakeACab");
}
}
Driver knows about getServices().
NormalCab needs to implement it.
Solving via Constructor Injection
class Driver {
private final Stream<String> services;
Driver(Stream<String> services) {
this.services = services; // Stored here
}
Stream<String> rank() {
return services.sorted();
}
}
class NormalCab extends Driver {
NormalCab() {
super(Stream.of("JustRide", "TakeACab"));
}
}