3 Replies Latest reply: Jan 16, 2013 1:20 AM by FXit RSS

    Using Builders.

    FXit
      Hi *,

      I would like to use builders, but when I use the fill() method it is no longer possible to call the build() method. Is this a bug or do I miss something.

      Many thanks.
      import java.util.logging.Level;
      import java.util.logging.Logger;
      import javafx.application.Application;
      import javafx.scene.Scene;
      import javafx.scene.layout.AnchorPane;
      import javafx.scene.paint.Color;
      import javafx.scene.text.Text;
      import javafx.scene.text.TextBuilder;
      import javafx.stage.Stage;
      
      public class BuilderTest extends Application {
      
           @Override
           public void start(Stage primaryStage) {
                try {
                     AnchorPane mainWindow = new AnchorPane();
      
                     @SuppressWarnings("rawtypes")
                     final TextBuilder builder = TextBuilder.create().x(50).fill(Color.BLACK);
      
                     final Text text1 = builder.text("Hello World!").y(50).build();
                     final Text text3 = builder.text("JavaFX is fun!").y(150).build();
                     
                     /*
                      * This is not working, but why ... ?
                      */
      //               final Text text2 = builder.text("Goodbye World!").fill(Color.BLUE).y(100).build();
                     
                     mainWindow.getChildren().addAll(text1, text3);
                     
                     Scene scene = new Scene(mainWindow, 400, 200);
                     primaryStage.setScene(scene);
                     primaryStage.setTitle("BuilderTest");
                     primaryStage.show();
                } catch (Exception ex) {
                     Logger.getLogger(BuilderTest.class.getName()).log(Level.SEVERE, null, ex);
                }
           }
      
           public static void main(String[] args) {
                launch(args);
           }
      }
        • 1. Re: Using Builders.
          984080
          Try using this instead:
              final Text text2 = builder.create()
                  .text("Goodbye World!")
                  .y(100)
                  .fill(Color.BLUE)
              .build();
          • 2. Re: Using Builders.
            James_D
            It doesn't compile, rather than doesn't work...

            The problem is a subtle one to do with generic types. The fill(...) method you are calling is defined in the ShapeBuilder class; it's return type is the generic type B in the class definition
            ShapeBuilder<B extends ShapeBuilder>
            Since you've declared your builder as a raw type, the compiler loses information as to how to resolve the type B. Thus, when you call fill(...) when assigning to text2, the result of the fill(...) call is a ShapeBuilder.

            The method y(...) is defined in TextBuilder, not in ShapeBuilder; so you get a compile error when trying to invoke it on what the compiler only knows is "some kind of ShapeBuilder".

            You can fix this by changing the declaration of the builder. Instead of
            TextBuilder builder = TextBuilder.create()....
            do
            TextBuilder<?> builder = TextBuilder.create()...
            (and you can now remove your "@SuppressWarnings" annotation).


            So why does this help?

            The declaration of the TextBuilder class is
            public class TextBuilder<B extends TextBuilder<B>> extends ShapeBuilder<B>
            By changing the declaration like this, you allow the compiler to know that builder is of type "TextBuilder of some unknown class B"; but the declaration of the TextBuilder class ensures that whatever B is, it's a subclass of TextBuilder. Thus the return type for the fill(...) method becomes "some unknown subclass of TextBuilder", and so the compiler allows you to invoke the method y(...) on the returned object.

            If you want to be more explicit, you can make the declaration
            TextBuilder<? extends TextBuilder<?>> builder = ...
            Here's a complete version that compiles (and works):
            import java.util.logging.Level;
            import java.util.logging.Logger;
            import javafx.application.Application;
            import javafx.scene.Scene;
            import javafx.scene.layout.AnchorPane;
            import javafx.scene.paint.Color;
            import javafx.scene.text.Text;
            import javafx.scene.text.TextBuilder;
            import javafx.stage.Stage;
            
            public class BuilderTest extends Application {
            
              @Override
              public void start(Stage primaryStage) {
                try {
                  AnchorPane mainWindow = new AnchorPane();
            
                  TextBuilder<? extends TextBuilder<?>> builder = TextBuilder.create().x(50).fill(Color.BLACK);
            
                  final Text text1 = builder.text("Hello World!").y(50).build();
                  final Text text3 = builder.text("JavaFX is more fun with generic types!").y(150).build();
            
                  /*
                   * This is not working, but why ... ?
                   */
                  final Text text2 = builder.text("Goodbye World!").fill(Color.BLUE).y(100)
                      .build();
            
                  mainWindow.getChildren().addAll(text1, text2, text3);
            
                  Scene scene = new Scene(mainWindow, 400, 200);
                  primaryStage.setScene(scene);
                  primaryStage.setTitle("BuilderTest");
                  primaryStage.show();
                } catch (Exception ex) {
                  Logger.getLogger(BuilderTest.class.getName()).log(Level.SEVERE, null, ex);
                }
              }
            
              public static void main(String[] args) {
                launch(args);
              }
            }
            Generic types are fun, huh?
            • 3. Re: Using Builders.
              FXit
              Hi James, thank you for the detailed answer, I now realize how it works. I have learned a lot. And yes, it was fun :-)