Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
jetbrains avatar

Java Engineer

  • 1 installs
  • 19 repo stars
  • Updated July 14, 2026
  • jetbrains/junie-extensions

java-engineer skill documents Java 21 language features, idioms, and coding standards.

About

java-engineer skill documents Java 21 language features, idioms, and coding standards. Use when writing Java code involving records, sealed classes, virtual threads, streams, or generics.. name: java-engineer description: Java 21 language features, idioms, and coding standards. Use when writing Java code involving records, sealed classes, virtual threads, streams, or generics.

  • Java 21 language features, idioms, and coding standards.
  • Platform-specific setup patterns for java-engineer.
  • Evidence-backed steps from upstream SKILL.md.
  • When-to-use criteria for java-engineer versus alternatives.

Java Engineer by the numbers

  • 1 all-time installs (skills.sh)
  • Ranked #642 of 782 Skill Development skills by installs in the Skillselion catalog
  • Data as of Jul 25, 2026 (Skillselion catalog sync)
At a glance

java-engineer capabilities & compatibility

Capabilities
java engineer quick start · java engineer when to use guidance · java engineer integration patterns
Use cases
documentation
IDEs
intellij · jetbrains
From the docs

What java-engineer says it does

public record Money(BigDecimal amount, Currency currency) {
SKILL.md
npx skills add https://github.com/jetbrains/junie-extensions --skill java-engineer

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs1
repo stars19
Last updatedJuly 14, 2026
Repositoryjetbrains/junie-extensions

How do I use java-engineer correctly?

Java 21 language features, idioms, and coding standards. Use when writing Java code involving records, sealed classes, virtual threads, streams, or generics.

Who is it for?

Teams implementing java-engineer workflows from the catalog.

Skip if: Skip when requirements clearly match a different specialized stack.

When should I use this skill?

User asks about java-engineer, java 21 language features, idioms, and coding standards. use when writing java code involv.

What you get

Working java-engineer setup with validated configuration and next steps.

Files

SKILL.mdMarkdownGitHub ↗

Java 21 — Language Features & Idioms

Records

// Immutable data carrier — generates constructor, accessors, equals, hashCode, toString
public record Money(BigDecimal amount, Currency currency) {
    // Compact constructor for validation
    public Money {
        Objects.requireNonNull(amount);
        if (amount.compareTo(BigDecimal.ZERO) < 0) throw new IllegalArgumentException("negative amount");
    }

    public Money add(Money other) {
        if (!currency.equals(other.currency)) throw new IllegalArgumentException("currency mismatch");
        return new Money(amount.add(other.amount), currency);
    }
}

// Use records for DTOs and value objects — not for mutable JPA entities
public record UserCreateRequest(
    @NotBlank String email,
    @NotBlank @Size(min = 8) String password
) {}

Sealed Classes & Pattern Matching

// Sealed hierarchy — exhaustive, compiler-enforced
public sealed interface PaymentResult
    permits PaymentResult.Success, PaymentResult.Failure, PaymentResult.Pending {}

public record Success(String transactionId) implements PaymentResult {}
public record Failure(String reason, ErrorCode code) implements PaymentResult {}
public record Pending(String referenceId) implements PaymentResult {}

// Pattern matching switch — no default needed when all permits are handled
String message = switch (result) {
    case Success s  -> "Paid: " + s.transactionId();
    case Failure f  -> "Failed: " + f.reason();
    case Pending p  -> "Pending: " + p.referenceId();
};

// Pattern matching instanceof — eliminates explicit cast
if (result instanceof Failure f && f.code() == ErrorCode.INSUFFICIENT_FUNDS) {
    log.warn("Insufficient funds for reference {}", f.referenceId());
}

Switch Expressions

// Arrow form — no fall-through, expression yields a value
int discount = switch (customer.tier()) {
    case GOLD    -> 20;
    case SILVER  -> 10;
    case BRONZE  -> 5;
    default      -> 0;
};

// yield for multi-statement branches
String label = switch (status) {
    case ACTIVE -> "Active";
    case INACTIVE -> {
        log.debug("Inactive customer: {}", customer.id());
        yield "Inactive";
    }
};

Text Blocks

String json = """
        {
            "name": "%s",
            "email": "%s"
        }
        """.formatted(user.name(), user.email());

String sql = """
        SELECT u.id, u.email
        FROM users u
        WHERE u.active = true
          AND u.created_at >= :since
        ORDER BY u.created_at DESC
        """;

Virtual Threads (Java 21)

// Structured concurrency — tasks live within the scope's lifetime
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
    Future<User>  user    = scope.fork(() -> userService.findById(userId));
    Future<Order> order   = scope.fork(() -> orderService.findLatest(userId));

    scope.join().throwIfFailed();

    return new DashboardResponse(user.get(), order.get());
}

// Virtual thread executor — cheap, non-pinning I/O concurrency
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();

Streams

// Transform + filter + collect
List<String> emails = users.stream()
    .filter(User::isActive)
    .map(User::email)
    .toList();  // Unmodifiable list, Java 16+

// Grouping
Map<Department, List<Employee>> byDept = employees.stream()
    .collect(Collectors.groupingBy(Employee::department));

// FlatMap for nested collections
List<String> allTags = posts.stream()
    .flatMap(post -> post.tags().stream())
    .distinct()
    .toList();

// Reduction
BigDecimal total = items.stream()
    .map(Item::price)
    .reduce(BigDecimal.ZERO, BigDecimal::add);

// ❌ Avoid complex nested streams — use loops for readability when logic exceeds 3 steps

Optional

// ✅ Return Optional from find* methods — signals "may be absent"
Optional<User> user = userRepository.findByEmail(email);

// ✅ Chain transformations
return userRepository.findByEmail(email)
    .map(UserResponse::from)
    .orElseThrow(() -> new ResourceNotFoundException("User not found: " + email));

// ✅ Default value
String displayName = user.map(User::displayName).orElse("Anonymous");

// ❌ Never use Optional as method parameter or field — only as return type
// ❌ Never call .get() without isPresent() check — use map/orElseThrow

Generics & Type Safety

// Bounded type parameter
public <T extends Comparable<T>> T max(List<T> items) {
    return items.stream().max(Comparator.naturalOrder())
        .orElseThrow(() -> new IllegalArgumentException("empty list"));
}

// Wildcard for read-only collections
public void printAll(List<? extends Shape> shapes) {
    shapes.forEach(s -> System.out.println(s.area()));
}

// Generic repository pattern
public interface Repository<T, ID> {
    Optional<T> findById(ID id);
    T save(T entity);
    void deleteById(ID id);
}

// ❌ Avoid raw types — always declare type parameters

Exceptions

// Domain-specific unchecked exceptions
public class ResourceNotFoundException extends RuntimeException {
    public ResourceNotFoundException(String message) { super(message); }
    public ResourceNotFoundException(String message, Throwable cause) { super(message, cause); }
}

// ✅ Wrap technical exceptions with context
try {
    return externalClient.fetch(id);
} catch (HttpClientErrorException ex) {
    throw new ResourceNotFoundException("External resource not found: " + id, ex);
}

// ✅ Fail fast — validate at boundaries
public Order createOrder(CreateOrderRequest request) {
    if (request.items().isEmpty()) throw new IllegalArgumentException("Order must have items");
    // ...
}

// ❌ Avoid catching Exception broadly unless logging+rethrowing
// ❌ Never swallow exceptions silently

Naming & Style

// Classes, Records, Interfaces: PascalCase
public class OrderService {}
public record Money(BigDecimal amount, Currency currency) {}
public interface PaymentGateway {}

// Methods, fields: camelCase
private final UserRepository userRepository;
public Optional<User> findByEmail(String email) {}

// Constants: UPPER_SNAKE_CASE
private static final int MAX_RETRY_ATTEMPTS = 3;
private static final Duration TIMEOUT = Duration.ofSeconds(5);

// Boolean methods: is/has/can prefix
public boolean isActive() {}
public boolean hasPermission(String role) {}

Immutability

// ✅ Final fields by default
public class UserService {
    private final UserRepository userRepository;
    private final PasswordEncoder passwordEncoder;

    public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder) {
        this.userRepository = userRepository;
        this.passwordEncoder = passwordEncoder;
    }
}

// ✅ Unmodifiable collections
private static final List<String> ALLOWED_ROLES = List.of("ADMIN", "USER", "MODERATOR");

// ✅ Copy on intake to prevent aliasing
public Order(List<OrderItem> items) {
    this.items = List.copyOf(items);
}

Logging

private static final Logger log = LoggerFactory.getLogger(OrderService.class);

// ✅ Structured — stable field names, parameterized
log.info("order_created orderId={} customerId={} total={}", order.id(), order.customerId(), order.total());
log.warn("payment_failed orderId={} reason={}", orderId, reason);
log.error("unexpected_error orderId={}", orderId, ex);  // exception as last arg

// ❌ Never log PII (emails, phone, address), tokens, or raw passwords
// ❌ No string concatenation in log calls — always use parameterized form

Null Handling

// ✅ Use @NonNull / @Nullable to document intent at API boundaries
public UserResponse findById(@NonNull Long id) { ... }
public @Nullable String getNickname() { ... }

// ✅ Validate parameters early
public void process(@NonNull Order order, @NonNull String currency) {
    Objects.requireNonNull(order, "order must not be null");
    Objects.requireNonNull(currency, "currency must not be null");
}

Code Smells to Avoid

  • Long parameter lists → use a request record or builder
  • Deep nesting → early returns and guard clauses
  • Magic numbers/strings → named constants
  • Static mutable state → dependency injection
  • Silent catch blocks → log and rethrow or convert to domain exception
  • instanceof chains → sealed classes with switch

Testing Standards

// JUnit 5 + AssertJ
@Test
void shouldRejectNegativeAmount() {
    assertThatThrownBy(() -> new Money(new BigDecimal("-1"), Currency.getInstance("USD")))
        .isInstanceOf(IllegalArgumentException.class)
        .hasMessageContaining("negative amount");
}

@Test
void shouldAddMoneyOfSameCurrency() {
    Money a = new Money(new BigDecimal("10.00"), Currency.getInstance("USD"));
    Money b = new Money(new BigDecimal("5.50"), Currency.getInstance("USD"));

    assertThat(a.add(b).amount()).isEqualByComparingTo("15.50");
}
  • Mockito for dependencies; avoid partial mocks (spy) unless unavoidable
  • Test method names: should<ExpectedBehavior>[When<Condition>]
  • One assertion focus per test — multiple assertThat on the same result is fine

Related skills

FAQ

What does java-engineer do?

java-engineer skill documents Java 21 language features, idioms, and coding standards.

When should I use java-engineer?

User asks about java-engineer, java 21 language features, idioms, and coding standards. use when writing java code involv.

Is this skill safe to install?

Review the Security Audits panel on this page before installing in production.

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.