Chaturmind
LearnDSASystem DesignInterview PrepDevOpsEngineering GrowthBlog
Start learning
Chaturmind

Structured learning paths for engineers who want to go deep. Written by practitioners.

Learn

  • Java
  • DSA
  • System Design
  • Spring Boot
  • AI / ML
  • DevOps
  • Engineering Growth
  • Java Interview Prep

Company

  • Blog
  • Contact

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Chaturmind. All rights reserved.

Built for engineers who want to go deep.


← Java Interview Prep: 5–8 Years

Advanced Core Java

  • Advanced OOP & Design Scenarios — Interview Questions
  • Advanced Concurrency, Collections & Memory — Interview Questions
  • Modern Java Language Features & Annotations — Interview Questions
  • Class Loading, Reflection, Serialization & Idioms — Interview Questions

Java Design Patterns in Depth

  • Design Patterns Overview & Singleton — Interview Questions
  • Factory & Abstract Factory Patterns — Interview Questions
  • Builder & Prototype Patterns — Interview Questions
  • Adapter & Bridge Patterns — Interview Questions
  • Composite & Decorator Patterns — Interview Questions
  • Facade & Proxy Patterns — Interview Questions
  • Chain of Responsibility & Observer Patterns — Interview Questions
  • Strategy & Template Method Patterns — Interview Questions
  • Command Pattern — Interview Questions

Advanced Spring Boot

  • Logging, Configuration & Actuator (Advanced) — Interview Questions
  • Transactions, Multiple Datasources & Query Tuning — Interview Questions
  • Validation & REST API Design (Advanced) — Interview Questions
  • Reactive, Async & Scheduling in Spring Boot — Interview Questions
  • Deployment, High Availability, Scaling & Caching — Interview Questions
Chaturmind
← Java Interview Prep: 5–8 Years

Advanced Core Java

  • Advanced OOP & Design Scenarios — Interview Questions
  • Advanced Concurrency, Collections & Memory — Interview Questions
  • Modern Java Language Features & Annotations — Interview Questions
  • Class Loading, Reflection, Serialization & Idioms — Interview Questions

Java Design Patterns in Depth

  • Design Patterns Overview & Singleton — Interview Questions
  • Factory & Abstract Factory Patterns — Interview Questions
  • Builder & Prototype Patterns — Interview Questions
  • Adapter & Bridge Patterns — Interview Questions
  • Composite & Decorator Patterns — Interview Questions
  • Facade & Proxy Patterns — Interview Questions
  • Chain of Responsibility & Observer Patterns — Interview Questions
  • Strategy & Template Method Patterns — Interview Questions
  • Command Pattern — Interview Questions

Advanced Spring Boot

  • Logging, Configuration & Actuator (Advanced) — Interview Questions
  • Transactions, Multiple Datasources & Query Tuning — Interview Questions
  • Validation & REST API Design (Advanced) — Interview Questions
  • Reactive, Async & Scheduling in Spring Boot — Interview Questions
  • Deployment, High Availability, Scaling & Caching — Interview Questions
HomeLearnJava Interview PrepJava Interview Prep: 5–8 YearsAdvanced Core Java
✓ FreeAdvanced· 9 min read

Modern Java Language Features & Annotations — Interview Questions

Why lambdas changed Java, generics for type safety and reuse, what streams really do for performance and maintainability, default methods and API evolution, interfaces vs abstract classes after Java 8, @Retention and @Target, the diamond problem, the recursive Enum<E extends Enum<E>> declaration, records and sealed classes.

Published September 25, 2026


How to use this lesson

Senior candidates are expected to discuss language features in terms of design impact. How does this feature change the APIs you design, the bugs you can prevent, and the performance you get? Get the Java versions right too (preview versus final), because interviewers notice.

Q1. Why are Java 8 lambdas considered such a big change?

Short answer: Lambdas made behaviour a first-class value. That changed how Java libraries are designed, not just how code is written:

  • Higher-order APIs became practical: streams, CompletableFuture, Comparator.comparing, Map.computeIfAbsent, and Spring's callback-style APIs.
  • A declarative style replaced a lot of boilerplate loops and anonymous classes.
  • They were implemented with invokedynamic, so they're cheap. Non-capturing lambdas are singletons, and the JIT can inline them.
  • They pushed design towards functional composition, immutability and strategies passed as parameters.

Learn it in depth → Lambdas & Functional Interfaces

Q2. How do generics maintain type safety and reduce duplication?

Short answer: Generics move type checks to compile time. A List<Order> can't receive a Customer, and reading from it needs no cast, so a whole class of runtime ClassCastExceptions disappears. One implementation serves every type, and bounded type parameters (<T extends Comparable<? super T>>) express constraints precisely.

public interface Repository<T, ID> {                       // one abstraction for every entity
    Optional<T> findById(ID id);
    T save(T entity);
}

public static <T extends Comparable<? super T>> T max(Collection<? extends T> items) {   // PECS
    return items.stream().max(Comparator.naturalOrder()).orElseThrow();
}

Key points to cover:

  • Know the limits of type erasure: no new T(), no generic arrays, and no instanceof List<String>. Work around them with Class<T> tokens, or supertype tokens.

Learn it in depth → Generics

Q3. How do streams and lambdas affect performance and maintainability?

Short answer: Maintainability: pipelines state the intent (filter, map, group) declaratively, compose well, and keep side effects out of view. Performance is mostly neutral for sequential streams: slightly slower than hand-written loops for tiny workloads, and comparable after JIT warm-up. It improves with primitive streams, with short-circuiting, and sometimes with parallel streams, on large, CPU-bound, easily split data.

Common trap: "streams make code faster because they run in parallel". Parallelism is opt-in. It uses the shared common ForkJoinPool, and often hurts in server code: small datasets, blocking I/O, contention with the request threads. Measure with JMH.

Q4. How do default methods affect the design and evolution of Java applications?

Short answer:

  • They make interface evolution possible. You can add methods to published interfaces without breaking implementers (as the JDK did with Collection.stream()).
  • They enable mixin-like behaviour composition (Comparator.reversed, Predicate.and).
  • They reduce the need for companion utility classes.

Design implications:

  • Default methods must work only through the interface's abstract methods, because interfaces have no state.
  • Changing a default method can silently change the behaviour of every implementer that doesn't override it.
  • Name clashes between interfaces must be resolved explicitly.

Learn it in depth → Default Methods & Optional

Q5. After Java 8, how do you choose between an interface and an abstract class?

Short answer: Default methods blurred the line, but the key difference remains state and construction.

  • Choose an interface for capabilities and contracts: multiple inheritance of type, easy mocking and proxying, and lambda compatibility if it's functional. That's the default choice.
  • Choose an abstract class when implementations share instance state, need constructors or invariants, need protected or non-public members, or follow a template method with a controlled algorithm.

A common combination is an interface for the contract plus an abstract skeletal implementation (AbstractList).

Key points to cover:

  • Sealed interfaces (Java 17) add a third option: a closed set of implementations. That's ideal for domain variants, together with pattern matching.

Q6. What is @Retention for?

Short answer: It declares how long an annotation survives:

  • SOURCE: discarded by the compiler. Used for compile-time tools: @Override, Lombok, annotation processors.
  • CLASS (the default): kept in the bytecode, but not visible through reflection. Used by bytecode tools.
  • RUNTIME: available through reflection at runtime. Required for Spring, JPA, Jackson and Bean Validation annotations, and for custom annotations read by aspects.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Audited { String action(); }

@Around("@annotation(audited)")                      // readable at runtime only because of RUNTIME retention
Object audit(ProceedingJoinPoint jp, Audited audited) throws Throwable { … }

Common trap: forgetting RUNTIME on a custom annotation. The aspect or reflection code then silently never sees it.

Q7. What does @Target do?

Short answer: It restricts where an annotation may be used: TYPE, METHOD, FIELD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE, TYPE_PARAMETER, TYPE_USE (any use of a type, as in List<@NonNull String>), MODULE and RECORD_COMPONENT. Misplacing the annotation becomes a compile error.

Key points to cover:

  • Related meta-annotations:
    • @Documented (appears in the Javadoc);
    • @Inherited (class annotations inherited by subclasses);
    • @Repeatable.
  • Composed annotations: Spring's @RestController is built from @Controller + @ResponseBody using ANNOTATION_TYPE.

Q8. Two interfaces have the same default method with different bodies. How do you resolve this diamond problem?

Short answer: The implementing class must override the method. Otherwise it's a compile error. Inside the override, delegate explicitly with InterfaceName.super.method(), combine both, or write new behaviour.

interface Loggable { default String describe() { return "loggable"; } }
interface Auditable { default String describe() { return "auditable"; } }
final class Invoice implements Loggable, Auditable {
    @Override public String describe() { return Auditable.super.describe(); }   // an explicit, documented choice
}

Key points to cover:

  • The rules: class methods win over interface defaults, and a more specific (sub-)interface wins over its parent. Only true ambiguity forces an override.

Q9. What's the significance of the generic declaration on the Enum class?

Short answer: It's declared as public abstract class Enum<E extends Enum<E>>, a recursive (F-bounded) generic. Each enum Color compiles to final class Color extends Enum<Color>, so methods inherited from Enum are typed to the concrete enum:

  • compareTo(E o) only accepts the same enum type (you can't compare a Color with a Size).
  • getDeclaringClass() returns Class<E>.
  • valueOf(Class<T>, String) and EnumSet<E extends Enum<E>> / EnumMap<K extends Enum<K>, V> get compile-time type safety.

Common trap: saying it's declared Enum<?>. The wildcard Enum<?> is how you refer to "some enum" in your own code. The declaration is the self-referential bound. The same pattern appears in fluent builders (Builder<T extends Builder<T>>).

Q10. What is a record, and when do you use it?

Short answer: A record (a preview in Java 14–15, final in Java 16) is a transparent, shallowly immutable data carrier. You declare its components, and the compiler generates the private final fields, the canonical constructor, the accessors (name(), not getName()), and equals, hashCode and toString. Records can have compact constructors for validation or normalisation, static factories, instance methods, and they can implement interfaces.

public record Money(BigDecimal amount, Currency currency) {
    public Money {                                         // compact constructor
        Objects.requireNonNull(currency);
        if (amount.scale() > currency.getDefaultFractionDigits()) throw new IllegalArgumentException("bad scale");
    }
    public Money plus(Money other) { return new Money(amount.add(other.amount), currency); }
}

Key points to cover:

  • Good uses: DTOs and API payloads, value objects, map keys, multiple return values, events.
  • Not suitable as JPA entities: they're final, with no no-arg constructor and no mutability. They do work as projections and @Embeddables (Hibernate 6.2+).
  • "Shallowly immutable" means collection components need List.copyOf.

Learn it in depth → Records

Q11. What is a sealed class, and when do you use it?

Short answer: A sealed class or interface (a preview in Java 15–16, final in Java 17) restricts which classes may extend or implement it, with a permits clause. Each permitted subclass must be final, sealed or non-sealed. That gives you a closed hierarchy, so the compiler knows every variant. Pattern-matching switch (Java 21) can then be exhaustive without a default, and adding a new variant turns every non-exhaustive switch into a compile error.

public sealed interface PaymentResult permits Approved, Declined, Pending { }
public record Approved(String reference) implements PaymentResult { }
public record Declined(String reason) implements PaymentResult { }
public record Pending(Duration retryAfter) implements PaymentResult { }

String message(PaymentResult r) {
    return switch (r) {                                    // exhaustive: no default needed
        case Approved a -> "Paid (" + a.reference() + ")";
        case Declined d -> "Declined: " + d.reason();
        case Pending p  -> "Processing, retry in " + p.retryAfter().toSeconds() + "s";
    };
}

Key points to cover:

  • Good uses: modelling domain alternatives (algebraic data types), protocol messages, results instead of exceptions, and restricting extension of framework types.

Learn it in depth → Sealed Classes

Follow-up questions this topic invites — and their answers

Q: What are record patterns? A: Java 21 lets you deconstruct records in instanceof and switch: case Approved(var ref) -> …, including nested patterns. Combined with sealed types, this enables concise data-oriented code.

Q: Can records have mutable fields? A: Their component fields are final, but a component can reference a mutable object (a List, an array). Defensive copies in the compact constructor keep a record effectively immutable.

Q: What's the difference between non-sealed and final for a permitted subclass? A: final closes the hierarchy at that point. non-sealed reopens it, so anyone can extend that subclass. It's a deliberate escape hatch.

Q: When would you write an annotation processor? A: To generate code or validate usage at compile time (like MapStruct, Lombok or Dagger). It needs SOURCE or CLASS retention, and it gives zero runtime reflection cost.

Previous

Advanced Concurrency, Collections & Memory — Interview Questions

Next

Class Loading, Reflection, Serialization & Idioms — Interview Questions

AI Tutor

Lesson: Modern Java Language Features & Annotations — Interview Questions

Quick actions

AI responses can be inaccurate. Verify critical information.