javagenericsvaadinvaadin-flow

Vaadin 24 compile issue with CustomField java generics


I'm upgrading a Vaadin 23.2 app to Vaadin 24.8. I have a compilation error on one of my components. It's a simple custom field with this signature:

public class MyField extends CustomField<String> {

IntelliJ IDEA doesn't show any error, but Gradle compilation (JDK17) throws:

error: HasValue cannot be inherited with different arguments: <?,java.lang.String> and <com.vaadin.flow.component.AbstractField.ComponentValueChangeEvent<?,java.lang.String>,java.lang.String>

without any indication of class name or line number, but when I remove the class the issue is gone.

I see that CustomField now effectively inherits from HasValue twice: by extending AbstractField and by implementing InputField, but I fail to see why these clash, and what I can do about that.

I'm on JDK17 (Eclipse Termurin), I've tried the latest Corretto 17 release but that doesn't help.

Update

In my custom field, I have three text fields:

    private final TextField part1 = new TextField();
    private final TextField part2 = new TextField();
    private final TextField part3 = new TextField();

I'm adding a focus listener to this and each part using this line:

        Stream.of(this, part1, part2, part3).forEach(c -> c.addFocusListener(event -> this.focused = true));

Commenting out that line makes the error go away.

When I hover over the c lambda parameter, IntelliJ IDEA shows its type:

com.vaadin.flow.component.AbstractField<? extends com.vaadin.flow.component.AbstractField<?, ?> & com.vaadin.flow.component.Focusable<? extends com.vaadin.flow.component.AbstractField<?, ?> & com.vaadin.flow.component.Focusable<?> & com.vaadin.flow.component.shared.InputField<? extends com.vaadin.flow.component.AbstractField.ComponentValueChangeEvent<? extends com.vaadin.flow.component.AbstractField<?, ?> & com.vaadin.flow.component.Focusable<?> & com.vaadin.flow.component.shared.InputField<?, ?> & com.vaadin.flow.component.HasTheme & com.vaadin.flow.component.shared.HasValidationProperties, String>, String> & com.vaadin.flow.component.HasTheme & com.vaadin.flow.component.shared.HasValidationProperties> & com.vaadin.flow.component.shared.InputField<? extends com.vaadin.flow.component.AbstractField.ComponentValueChangeEvent<? extends com.vaadin.flow.component.AbstractField<?, ?> & com.vaadin.flow.component.Focusable<?> & com.vaadin.flow.component.shared.InputField<?, ?> & com.vaadin.flow.component.HasTheme & com.vaadin.flow.component.shared.HasValidationProperties, String>, String> & com.vaadin.flow.component.HasTheme & com.vaadin.flow.component.shared.HasValidationProperties, String> & com.vaadin.flow.component.Focusable<? extends com.vaadin.flow.component.AbstractField<?, ?> & com.vaadin.flow.component.Focusable<?> & com.vaadin.flow.component.shared.InputField<? extends com.vaadin.flow.component.AbstractField.ComponentValueChangeEvent<? extends com.vaadin.flow.component.AbstractField<?, ?> & com.vaadin.flow.component.Focusable<?> & com.vaadin.flow.component.shared.InputField<?, ?> & com.vaadin.flow.component.HasTheme & com.vaadin.flow.component.shared.HasValidationProperties, String>, String> & com.vaadin.flow.component.HasTheme & com.vaadin.flow.component.shared.HasValidationProperties> & com.vaadin.flow.component.shared.InputField<? extends com.vaadin.flow.component.AbstractField.ComponentValueChangeEvent<? extends com.vaadin.flow.component.AbstractField<?, ?> & com.vaadin.flow.component.Focusable<?> & com.vaadin.flow.component.shared.InputField<?, ?> & com.vaadin.flow.component.HasTheme & com.vaadin.flow.component.shared.HasValidationProperties, String>, String> & com.vaadin.flow.component.HasTheme & com.vaadin.flow.component.shared.HasValidationProperties c

Solution

  • I can just add the focus listerens to this and the parts separately:

    this.addFocusListener(event -> this.focused = true);
    Stream.of(part1, part2, part3).forEach(c -> 
        c.addFocusListener(event -> this.focused = true));
    

    Or I can first assign the Stream to a simpler type instead of the enormous inferred type:

    Stream<Focusable<?>> stream = Stream.of(this, part1, part2, part3);
    stream.forEach(c -> c.addFocusListener(event -> this.focused = true));
    

    Or just cast this first:

    Stream.of((Focusable<?>)this, part1, part2, part3).forEach(c -> c.addFocusListener(event -> this.focused = true));
    

    But probably the most idiomatic way without creating extra variables, is specifying the type when calling the static Stream.of method:

    Stream.<Focusable<?>>of(this, part1, part2, part3).forEach(c -> c.addFocusListener(event -> this.focused = true));
    

    In any case, a Java generics / type inference issue, not a Vaadin issue.