Using object-oriented concepts and functional interfaces
OOP pillars in Java
Encapsulation uses private fields and public accessors; inheritance uses extends and super; polymorphism resolves overridden instance methods at runtime; abstraction uses abstract classes and interfaces. A class extends one class but implements many interfaces. Interface default methods are inherited, static interface methods are not, and private interface methods support code reuse inside the interface. Java 17 sealed types use sealed, permits, non-sealed, and final to restrict subclasses. Records are final, generate accessors, equals, hashCode, toString, and a canonical constructor; compact constructors validate and assign fields.
Functional interfaces
A functional interface has exactly one abstract method. Key types: Predicate.test returns boolean, Function<T,R>.apply, BiFunction<T,U,R>.apply, Supplier.get, Consumer.accept, UnaryOperator, and BinaryOperator. Lambdas can capture local variables only if they are effectively final.
Worked example
record Range(int lo, int hi) { Range { if (lo > hi) throw new IllegalArgumentException(); } } The compact constructor has no parameter list and no return type; it can reassign parameters before implicit field assignment.
Traps
Static methods are hidden, not overridden. Private methods are not polymorphic. A lambda cannot change a captured local. Records cannot extend another class. equals/hashCode contracts must stay consistent. Adding a second abstract method breaks a lambda target.