0

i was trying to learn Tableview and got some example.

i dont get how StringProperty works.

Although class Person's fields are final instanace,

setEmailButton can change its value.

    import javafx.application.Application;
    import javafx.beans.property.*;
    import javafx.collections.*;
    import javafx.event.*;
    import javafx.geometry.Insets;
    import javafx.scene.*;
    import javafx.scene.control.*;
    import javafx.scene.control.cell.PropertyValueFactory;
    import javafx.scene.layout.VBox;
    import javafx.scene.text.Font;
    import javafx.stage.Stage;

    public class PropertyBasedTableView extends Application {
      private TableView<Person> table = new TableView<Person>();
      private final ObservableList<Person> data = FXCollections.observableArrayList();
      private void initData() {
        data.setAll(
          new Person("Jacob", "Smith", "jacob.smith@example.com"),
          new Person("Isabella", "Johnson", "isabella.johnson@example.com"),
          new Person("Ethan", "Williams", "ethan.williams@example.com")
        );
      }

      public static void main(String[] args) { launch(args); }

      @Override public void start(Stage stage) {
        initData();

        stage.setTitle("Table View Sample");
        stage.setWidth(450);
        stage.setHeight(500);

        final Label label = new Label("Address Book");
        label.setFont(new Font("Arial", 20));

        TableColumn firstNameCol = new TableColumn("First Name");
        firstNameCol.setMinWidth(100);
        firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));

        TableColumn lastNameCol = new TableColumn("Last Name");
        lastNameCol.setMinWidth(100);
        lastNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));

        TableColumn emailCol = new TableColumn("Email");
        emailCol.setMinWidth(200);
        emailCol.setCellValueFactory(new PropertyValueFactory<Person, String>("email"));

        table.setItems(data);
        table.getColumns().addAll(firstNameCol, lastNameCol, emailCol);
        table.setPrefHeight(300);

        final Button setEmailButton = new Button("Set first email in table to wizard@frobozz.com");
        setEmailButton.setOnAction(new EventHandler<ActionEvent>() {
          @Override public void handle(ActionEvent event) {
            if (data.size() > 0) {
              data.get(0).setEmail("wizard@frobozz.com");
            }  
          }
        });


        final VBox vbox = new VBox(10);
        vbox.setPadding(new Insets(10, 0, 0, 10));
        vbox.getChildren().addAll(label, table, setEmailButton);

        stage.setScene(new Scene(new Group(vbox)));
        stage.show();
      }

      public static class Person {
        private final StringProperty firstName;
        private final StringProperty lastName;
        private final StringProperty email;

        private Person(String fName, String lName, String email) {
          this.firstName = new SimpleStringProperty(fName);
          this.lastName = new SimpleStringProperty(lName);
          this.email = new SimpleStringProperty(email);
        }

        public String getFirstName() { return firstName.get(); }
        public void setFirstName(String fName) { firstName.set(fName); }
        public StringProperty firstNameProperty() { return firstName; }
        public String getLastName() { return lastName.get(); }
        public void setLastName(String lName) { lastName.set(lName); }
        public StringProperty lastNameProperty() { return lastName; }
        public String getEmail() { return email.get(); }
        public void setEmail(String inMail) { email.set(inMail); }
        public StringProperty emailProperty() { return email; }  // if this method is commented out then the tableview will not refresh when the email is set.
      }
    }

then i made conclusion myself "eureka! final StringProperty type value can be changed!"

so i had test

    package zzzzDelete;

    import javafx.application.Application;
    import javafx.beans.property.SimpleStringProperty;
    import javafx.beans.property.StringProperty;
    import javafx.stage.Stage;

    class A{

        void someTest(){
            B insB = new B("why");
            System.out.println(insB.getString());
            insB.setString("omg");
            System.out.println(insB.getString());
        }

        class B{
            private final StringProperty someString;
            private B(String someString){
                this.someString = new SimpleStringProperty(someString);
            }

            public String getString(){
                return someString.get();
            }

            public void setString(String newString){
                this.someString = new SimpleStringProperty(newString);      // error
            }

        }
    }

    public class SomeTest {
        public static void main(String[] args){
            A a = new A();
            a.someTest();
        }
    }

error is occured because of final keyword.

i'm very confused between first and second example.

흠좀무
  • 1
  • 1
  • 2

2 Answers2

0

You can't change an immutable (final) field after it has been initialized (which happens in the constructor). Instead, just set the value of the StringProperty via setValue(String):

class B {
    private final StringProperty someString;

    private B(String someString){
        this.someString = new SimpleStringProperty(someString);
    }

    public String getString(){
        return someString.get();
    }

    public void setString(String newString){
        this.someString.setValue(newString);
    }

    public StringProperty stringProperty() {
        return this.someString;
    }
}

Read the JavaFX: Properties and Binding Tutorial to see how to use JavaFX properties.

The following picture should clarify how changing the StringProperty works:

reference someString (red) is final and cannot be changed

Your object of type B always references the same StringProperty object, because the reference someString is final and cannot be changed (see red arrow). But the SimpleStringProperty object that is referenced by someString is mutable. It holds a String reference named value (see green arrow) that can be changed to point to another String object, e.g. by calling the setValue("second") (see green dotted arrow).

isnot2bad
  • 24,105
  • 2
  • 29
  • 50
  • sorry for bad question. and thanks. but still i don't get little. So i can change "the final value" via propertymethod(setString) with ".set" ? i learned final keyword can't be changed and should not be changed. thats what i'm confused. – 흠좀무 Dec 07 '14 at 03:00
  • No you can't change the reference `someString`, but you can change the object that is referenced by it. – isnot2bad Dec 07 '14 at 04:02
0

When you label a variable as final, it can only be assigned a value once. In your second example, you get a compile error because you try to assign a value to someString in the setString(...) method:

this.someString = new SimpleStringProperty(newString);

This is not allowed, because the setString(...) method can be invoked multiple times on the same object.

By contrast, in your first example, the only time any of the final variables (instance fields) are assigned values is in the constructor, which of course can only be called once for any given object.

Note there is a difference between assigning a value to a reference:

this.someString = new SimpleStringProperty(...);

and changing the state of the object to which the reference points:

this.firstName.set(fName);

The second is perfectly fine even if firstName is final.

If you write class B in the second example to follow the same pattern as the Person class in the first example, it will work fine.

James_D
  • 201,275
  • 16
  • 291
  • 322
  • sorry for bad question. and thanks. but still i don't get little. So i can change "the final value" via propertymethod(setString) with ".set" ? – 흠좀무 Dec 07 '14 at 02:58
  • You're not changing the value of the reference. You're changing the value of the string contained by the object the reference points to. The point is that in the `Person` class, the `setEmail(...)` method doesn't include `email = ...`, but in your class `B`, the `setString(...)` method includes `someString = ...;` – James_D Dec 07 '14 at 02:59
  • figure of speech. is StringProperty kind of bucket? i can't changed the bucket because of 'final' but i can change content(someString) in bucket. is it right thought? – 흠좀무 Dec 07 '14 at 03:06
  • Yes, that's a reasonable analogy. A more standard description is that `someString` is a reference. Since it's `final`, you can't change the value of the reference (i.e. it always refers to the same bucket), but you can change the state of the object to which it refers (you can change the contents of the bucket as you need). – James_D Dec 07 '14 at 03:08
  • thanks your big help. god bless you and i pray for your long live – 흠좀무 Dec 07 '14 at 03:11