Search code examples
javajavafxadapter

Unchecked Assignment error for generic JavaBeanObjectProperty but not for ReadOnlyJavaBeanObjectProperty


I'm struggling to understand what I need to do to not loose type information when building a JavaBeanObjectProperty for generic type T. One interesting detail is that I don't get this warning from IntelliJ for the ReadOnlyJavaBeanObjectProperty.

public class FooWrapper<T> {
    /**
     * Decorates a Foo instance with javafx stuff
     */
    private final Foo foo;

    private final JavaBeanObjectProperty<T> value;
    private final ReadOnlyJavaBeanObjectProperty<T> type;

    public ParameterWrapper(Foo toWrap) {
        this.foo = toWrap;
        try {
            this.value = new JavaBeanObjectPropertyBuilder<T>()
                    .bean(this.foo)
                    .name("value")
                    .build();
            this.type = new ReadOnlyJavaBeanObjectPropertyBuilder<T>()
                    .bean(this.foo)
                    .name("type")
                    .build();
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }
}

I have read Java unchecked operation cast to generic and Java Generics, how to avoid unchecked assignment warning when using class hierarchy? and am none the wiser.


Solution

  • The unchecked warnings are not your fault. The generic information is not kept because the API was poorly implemented. There is an unresolved issue regarding this: JDK-8152399. The issue is labeled as an "enhancement" but I'd consider it a bug.

    The problem is caused by the fact nearly all the methods of JavaBeanObjectPropertyBuilder return raw types. The only exception to this is build which returns JavaBeanObjectProperty<T>. This doesn't matter, however, because by the time you've called build you've lost the type information from calling bean and name. Unfortunately, this means there's nothing you can do to legitimately get rid of the warning—you can only suppress it.

    public class FooWrapper {
    
      private final Foo delegate;
    
      private final JavaBeanObjectProperty<SomeType> value;
    
      @SuppressWarnings("unchecked") // Reason: Bad API
      public FooWrapper(Foo delegate) {
        this.delegate = delegate;
        try {
          value = JavaBeanObjectPropertyBuilder.create()
              .bean(delegate)
              .name("value")
              .build();
        } catch (NoSuchMethodException ex) {
          throw new RuntimeException(ex);
        }
      }
    
    }
    

    This uses @SuppressWarnings to suppress the unchecked warnings. You can read more about it here. It's good practice to apply @SuppressWarnings to the narrowest scope possible so as to avoid suppressing other, useful warnings.


    For comparison, look at the method signatures of ReadOnlyJavaBeanObjectPropertyBuilder. They all return the generic type.