I have the following class GroupStudentsBySurname and it contains the following data structure HashMap<String, ArrayList<Student>>.
Each Student object contains two attributes: surname and english_score
The keys are the surname of students taken from the Student object Student.surname. For a particular key Lee, it has an ArrayList<Student> where the Students share the same surname.
One of the method of this GroupStudentsBySurname class is to compute the average_english_score for Students having the same surname.
I would use a TableView data structure for the class GroupStudentsBySurname, which looks like the following:
Surname | average_english_score <- column headings
Lee | 65
Chan | 86
Smith | 76
I want to be able to use track the changes of class GroupStudentsBySurname every time I add/delete a Student object from the arraylist, which in turn affects the average_english_score attribute.
Question: I do not know what data structure from javafx I should use in this case? Would I need to use ObservableMap or ObservableList to track the changes whenever I add / delete a Student object.
JavaFX is a UI technology, so not entirely sure what you mean be 'data structure from JavaFX'.
Anyway, let's start with the data model (we'll get to the UI later).
First I think you need to introduce another class (which requires a valid implementation of equals and hashCode on Student):
public class StudentGroup
{
private Set<Student> students = new HashSet<>();
private BigDecimal englishTotal = BigDecimal.ZERO;
public BigDecimal getEnglishAverage()
{
return englishTotal.divide(students.size());
}
public Collection<Student> getStudents()
{
return Collections.unmodifiableCollection(students);
}
public void addStudent(Student student)
{
if (students.add(student))
{
englishTotal.add(student.getEnglishScore());
}
}
public void removeStudent(Student student)
{
if (students.remove(student))
{
englishTotal.subtract(student.getEnglishScore());
}
}
}
Your class GroupStudentsBySurname then becomes:
public class GroupStudentsBySurname
{
private Map<String, StudentGroup> students = new HashMap<>();
...
}
Then create an adapter row class, which will allow StudentGroup to be used in more grouping scenarios than just being grouped by surname and is what will be used with JavaFX:
public class StudentBySurnameRow
{
private SimpleStringProperty surname;
private SimpleStringProperty groupSize;
private SimpleStringProperty englishAverage;
public StudentBySurnameRow(String surname, StudentGroup studentGroup)
{
this.surname = new SimpleStringProperty(surname);
this.groupSize = new SimpleStringProperty(Integer.toString(studentGroup.getStudents().size()));
this.englishAverage = new SimpleStringProperty(studentGroup.getEnglishAverage().toString());
}
...
}
Once you have these classes they can be slotted into a JavaFX TableView as per the Oracle TableView tutorial where you would create an ObservableList like this:
List<StudentBySurnameRow> studentBySurnameRows = new ArrayList<>();
for (Map.Entry<String, StudentGroup> entry : groupStudentsBySurname.getStudents().entrySet())
{
studentBySurnameRows.add(new StudentBySurnameRow(entry.getKey(), entry.getValue()));
}
table.setItems(FXCollections.observableArrayList(studentBySurnameRows));
I would use either a ListView or a TableView. In your case, if you just want to be able to manipulate the list, the first would be more practical. Otherwise, if you really want to use the whole map, the latter.
Related
Take an example i have three tables like this.
database image
How cloud i map the third table to java object.
class StudentCourse{
Student student;
Course course;
Double score;
}
or
class StudentCourse{
Long studentId;
Long courseId;
Double score;
}
if i use the first one, after i update some datas in databse such as student informations.The next time i query StudentCourse from database(I use mybatis) will the cache cause the incorrect data?
if i use the second one, if i want to list student's course scores,i have to first query the list of StudentCourse and then query the course's information from database through courseId, for each result i need additional queries. I think that will reduce the efficiency of the program.
Is there another way to solve this problem?
For the first one.
The second time mybatis do query, if the data hasn't been updated yet, it will get result from cache.
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
this.localCache.putObject(key, ExecutionPlaceholder.EXECUTION_PLACEHOLDER);
List list;
try {
list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
this.localCache.removeObject(key);
}
this.localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
this.localOutputParameterCache.putObject(key, parameter);
}
return list;
}
If i have an resultMap like this
<resultMap id="studentcourse" type="StudentCourse">
<association property="student" resultMap="Student" />
<association property="course" resultMap="Course"/>
<result property="score" column="score"/>
</resultMap>
At first i get an StudentCourse object from database, and the localCache cache the object.And then i update the Course in StudentCourse(change the database record).The second time i get the some StudentCourse it will return an result from localcache.So the course information in StudentCourse is dirty data.How to deal with it if i choose the first one.
Ideally you would use a class design that best models your domain and worry about mapping to a data store in a separate persistence layer. If you need to substantially change your model to allow the persistence layer to function then you need a new ORM! While I'm not familiar with mybatis I would hope it wouldn't create a new object each time the underlying data is changed.
The keys in the course and student tables act as foreign keys in the student_course table. Foreign keys are best represented as references in Java. To use the keys at the Java level forces an extra level of indirection and open you to integrity issues (e.g. if the foreign key changes).
So I would suggest:
class StudentCourse {
private final Student student;
private final Course course;
private double score;
}
You could also consider having it inside one of the other classes - that might be more convenient depending on how the classes are used:
class Student {
private final int id;
private String name;
private List<CourseScores> scores = new ArrayList<>();
public void addCourseScore(Course course, double score) {
scores.add(new CourseScore(course, score));
}
private record CourseScores(Course course, double score) { };
}
If your ORM doesn't resolve the keys for you (i.e. look up the referred object automatically when it retrieves data) then you'll need to do that yourself. It's a pretty simple object however:
class College {
private Map<Integer,Student> students;
private Map<Integer,Course> courses;
}
So the code to convert student_course data into the model above might look like:
ResultSet data;
while (!data.isAfterLast()) {
Student student = college.getStudent(data.getInteger("student"));
Course course = college.getCourse(data.getInteger("course"));
double score = data.getDouble("score");
student.addCourseScore(course, score);
data.next();
}
I have a class that creates index cards, and within it, I have an instance variable that is a static HashMap that stores all the instances created.
I have been thinking a lot about it and I thought that the methods that handle the opperations over that HashMap should go in a different class, because those methods don't opperate directly over any index card, they opperate over the list of index cards.
This way, I would have an IndexCard class, and an ListAdministrator class. And both classes would handle different functions.
The problem is that this new class (ListAdministrator) would only have static methods, because there is only one list and there is no reason to create any new list of index cards, I only need one.
Should I move those methods to another class or should I keep it like this? Is that a good practice?
This is the code:
class IndexCard {
public static HashMap <String, IndexCard> list = new HashMap <> ();
public String name;
public String address;
public String phone;
public String email;
public LocalDate dateRegister;
IndexCard(String name, String dni, String address, String phone, String email) {
this.name = name;
this.address = address;
this.phone = phone;
this.email = email;
dateRegister = LocalDate.now();
if (Utils.validarDni(dni) && !list.containsKey(dni)) {
list.put(dni, this);
} else {
throw new InvalidParameterException ("Error when entering the data or the DNI has already been previously registered");
}
}
/**
* Update the data of the selected card.
*/
public void update() throws IllegalAccessException {
String key = getKeyWithObject(this);
Scanner reader = new Scanner(System.in);
Field[] fields = this.getClass().getFields();
for (Field field: fields) {
String nameField = Utils.splitCamelCase(field.getName());
if (!Modifier.isStatic(field.getModifiers()) && (field.getType()).equals(String.class)) {
System.out.println ("Enter new " + nameField);
String value = reader.nextLine().trim();
field.set(this, value);
}
}
reader.close();
list.put(key, this);
System.out.println("Updated data \n \n");
}
/**
* Delete the selected card.
*/
public void delete() throws IllegalAccessException {
String key = getKeyWithObject(this);
Field [] fields = this.getClass().getFields();
for (Field field: fields) {
if (!Modifier.isStatic(field.getModifiers())) {
field.set(this, null);
}
}
list.remove(key);
}
/**
* Displays the data of the selected card on screen.
*/
public void print() throws IllegalAccessException {
Field [] fields = this.getClass().getFields();
for (Field field: fields) {
if (!Modifier.isStatic(field.getModifiers())) {
String nameFieldConSpaces = Utils.splitCamelCase(field.getName());
Object value = field.get(this);
System.out.println(nameFieldConSpaces + ":" + value);
}
}
}
/**
* Print all the entries of the desired sublist with the ID, Name and phone number.
*/
public static <T extends IndexCard> void SubClasslist (Class <T> subClass) {
for (HashMap.Entry <String, IndexCard> entry: list.entrySet ()) {
String key = entry.getKey ();
IndexCard card = entry.getValue ();
if (card.getClass().equals(subClass)) {
System.out.println ("ID:" + key + "| Name:" + card.name + "| Phone:" + card.phone);
}
}
}
/**
* Returns the object stored in the list of cards when entering the corresponding key.
*/
public static IndexCard GetObjetWithKey(String key) {
try {
return list.get(key);
} catch (IllegalArgumentException e) {
System.out.println (e + ": The indicated key does not appear in the database.");
return null;
}
}
/**
* Obtain the Key when entering the corresponding card.
*/
public static String getKeyWithObject (Object obj) {
for (HashMap.Entry <String, IndexCard> entry: list.entrySet()) {
if (obj.equals(entry.getValue())) {
return entry.getKey();
}
}
throw new IllegalArgumentException ("The indicated data does not appear in the database, and therefore we could not obtain the key.");
}
/**
* Returns a list of cards when entering the main data of the card.
* #param data Corresponds to the identifying name of the file.
*/
public static ArrayList <IndexCard> SearchByName (String data) {
try {
ArrayList <IndexCard> listCards = new ArrayList <> ();
for (HashMap.Entry <String, IndexCard> entry: list.entrySet ()) {
IndexCard card = entry.getValue ();
String name = entry.getValue().name;
if (name.toLowerCase().trim().contains(data.toLowerCase().trim())) {
listCards.add(card);
}
}
return listCards;
} catch (IllegalArgumentException e) {
System.out.println (e + "The indicated data does not appear in the database, you may have entered it incorrectly.");
return null;
}
}
}
All those static methods are what I would put in the new class.
This is how the new class ListAdministrator would look. It would not even need a constructor.
class ListAdministrator{
public static HashMap <String, IndexCard> list = new HashMap <> ();
/**
* Print all the entries of the desired sublist with the ID, Name and phone number.
*/
public static <T extends IndexCard> void SubClasslist (Class <T> subClass) {
for (HashMap.Entry <String, IndexCard> entry: list.entrySet ()) {
String key = entry.getKey ();
IndexCard card = entry.getValue ();
if (card.getClass().equals(subClass)) {
System.out.println ("ID:" + key + "| Name:" + card.name + "| Phone:" + card.phone);
}
}
}
/**
* Returns the object stored in the list of cards when entering the corresponding key.
*/
public static IndexCard GetObjetWithKey(String key) {
try {
return list.get(key);
} catch (IllegalArgumentException e) {
System.out.println (e + ": The indicated key does not appear in the database.");
return null;
}
}
/**
* Obtain the Key when entering the corresponding card.
*/
public static String getKeyWithObject (Object obj) {
for (HashMap.Entry <String, IndexCard> entry: list.entrySet()) {
if (obj.equals(entry.getValue())) {
return entry.getKey();
}
}
throw new IllegalArgumentException ("The indicated data does not appear in the database, and therefore we could not obtain the key.");
}
/**
* Returns a list of cards when entering the main data of the card.
* #param data Corresponds to the identifying name of the file.
*/
public static ArrayList <IndexCard> SearchByName (String data) {
try {
ArrayList <IndexCard> listCards = new ArrayList <> ();
for (HashMap.Entry <String, IndexCard> entry: list.entrySet ()) {
IndexCard card = entry.getValue ();
String name = entry.getValue().name;
if (name.toLowerCase().trim().contains(data.toLowerCase().trim())) {
listCards.add(card);
}
}
return listCards;
} catch (IllegalArgumentException e) {
System.out.println (e + "The indicated data does not appear in the database, you may have entered it incorrectly.");
return null;
}
}
}
You should keep the concerns of managing the IndexCards and the IndexCards themselves separate because of the Single Responsibility Principle. Furthermore the ListAdministrator should handle everything that deals with the management of the IndexCards, also deletion and creation of the managed objects.
The name ListAdministrator is somehow not meeting the point as it does not administrate lists, maybe use something like IndexCardRegistry.
To deal with concurrency you could use a ConcurrentMap as your main data storage.
Having ListAdministrator all static might come in handy if your IndexCards need access to it or other IndexCards, but this would not be the best design. Do they need to know anyway? From my understanding the IndexCards could be simple POJOs that contain only data and no logic at all.
On the other hand with an all-static ListAdministrator you will not be able to use two instances of managed objects at the same time in the future without major refactoring your code. Even if you never would expect this today a well defined object registry that can handle any object might come in handy in projects to come. Therefore I would rather use real instances for the ListAdministrator (and program against it's interface to stay flexible).
In more detail referring to your comments:
The idea of this approach is to keep concerns clearly separated, which will make future changes to your code feasible in case the project grows (most projects tend to do so). My understanding is that the ListAdministrator should manage your IndexCards. In a way this is the same as Object Relational Mappers work, but at the moment your database is a HashMap. If you create an interface for ListAdministrator you may even swap out the HashMap with a database without having to change its clients.
On second investigation of your code I found that IndexCards not only store the data but as well have methods to update the data. This represents another break of the Single Responsibility Principle and should be dealt with. If the ListAdministrator would provide an update method for a given IndexCard it could be used by as many different clients you can think of without changing any code behind the ListAdministrators API. Your first client would be the command-line interface you already have programmed, the next might be a web service.
With an all-static ListAdministrator you have one static Class that manages one static data set. It will always only deal with IndexCards, everything you add will end up in the same HashMap (if allowed/compatible). Every part of your application with access to the class ListAdministrator would have full access to the data. If you needed another ListAdministrator (handling create, delete, update, search) for a different type you would have to refactor everything to accomodate this or start duplicating code. Why not create an instance based solution in the first place. You would have your repository for IndexCards, and could add new repositories at will.
Maybe this is over-engineering for your use case but in keeping the responsibilities clearly separated you will find out that many extensions of your code will happen orthogonal (not affecting existing code), and this is where the fun really begins. And how do you want to practice this if not with smaller projects.
More details about the reason of using interfaces for flexible code (in response to latest comment)
The short answer is: always code against an interface (as stated in numerous articles and java books). But why?
A Java interface is like a contract between a class and its clients. It defines some methods, but does not implement them itself. To implement an interface you define a class with class XYZ implements SomeInterface and the source code of the class does whatever it finds reasonable to answer to the methods defined in the interface. You try to keep the interface small, to contain only the essential methods because the smaller the interface is, the less methods you have to take into account when changes have to be made.
A common idiom in Java would be to define a List<T> return type (the interface) for a method, which most likely would be an ArrayList (concrete class), but could be a LinkedList (another concrete class) as well, or anything else that implements the List interface. By just returning the List interface you prevent your client to use other methods of the otherwise returned concrete class as well which would greatly reduce your freedom to change the internal implementation of your "ListProvider". You hide the internal implementation but agree to return something that fulfills the given interface. If you want to conceed to even less obligations, you could return the interface Iteratable instead of List.
Checkout the Java API, you will find standard classes like ArrayList implement many interfaces. You could always use an ArrayList internally and return it as the smallest interface possible to do the job.
Back to your project. It would be essential to refer to the Registry (ListAdministrator) via its interface, not its concrete class. The interface would define methods like
interface IndexCardRegistry {
void delete(Long id) throws IllegalAccessException;
void update(Long id, Some data) throws IllegalAccessException;
// ...
}
What it does is of no concern for the client, it just hopes everything goes right. So if a client calls the repositories update method it would rely on the repository to update the targeted IndexCard. The repository could store the data as it wants, in a HashMap, in a List or even in a database, it would not matter to the clients.
class IndexCardMapBasedRegistry implements IndexCardRegistry {
private Map store = new HashMap();
void delete(Long id) throws IllegalAccessException {
// code to remove the IndexCard with id from the hashmap
}
void update(Long id, Some data) throws IllegalAccessException {
// code to get the IndexCard with id from
// the hashmap and update its contents
}
// ...
}
Now the new iteration, at creation of your registry you swap out IndexCardMapBasedRegistry for the new
class IndexCardDatabaseRegistry implements IndexCardRegistry {
private Database db;
void delete(Long id) throws IllegalAccessException {
// code to remove the IndexCard with id from the database
}
void update(Long id, Some data) throws IllegalAccessException {
// code to update the IndexCard with id in the database
}
// ...
}
IndexCardRegistry indexCards = new IndexCardMapBasedRegistry(); becomes
IndexCardRegistry indexCards = new IndexCardDatabaseRegistry();
The client must not change at all, but the Registry would be able to handle an amount of IndexCards that otherwise would blow your computers memory.
Stay with IndexCard class and dont need to create new class ListAdministrator
In class IndexCard you have list as of type hashmap and it represent in memory data structure and you have n number of method in this class to work in this data structure so i suggest stay with single class as it will serve single responsibility.
I know how to make a collection unmodifiable in java but I dont understand the need for such a method to exist. Can someone please explain this with an example scenario where I would want to make my collection unmodifiable?
Thanks in advance
The most efficient way to share private data outside of a class is to simply return it. But then something outside of the class can change the data that the class depends on. Another option is to copy the data before you share. This takes time and memory to do. Unmodifiable collections will often wrap the data and simply present it without allowing an outside class to edit it. This is faster than making a copy. An outside class can optionally make a modifiable copy if it needs.
An unmodifiable collection is basically read-only which is exactly what you want in case you need to publish such collection to client code and you don't want the client code to modify the collection.
It also promotes immutability which is generally a good thing since you won't have to care about the state of the collection for the rest of the execution of your program. See item 15 of Effective Java (2nd Edition) : Minimize mutability, and to quote Joshua Bloch :
Immutable objects are simple. An immutable object can be in exactly
one state, the state in which it was created.
Note that an unmodifiable collection will not make the contained objects immutable. This is a property each of the contained objects needs to make sure of, if it is required of course.
Take a look at this scenario. There is an application that creates 2 users, and then wants to notify them about something. But only users with name different from Peter should get the notification.
So we have to User.class:
public class User {
private String name;
private Integer id;
public User(final Integer id, final String name) {
this.id = id;
this.name = name;
}
public String getName() {
return name;
}
public Integer getId() {
return id;
}
}
The users are stored in special holder class (containing map):
public class UsersHolder {
private static Map<Integer, User> usersMap = new HashMap<Integer, User>();
public static void addUser(final User user) {
usersMap.put(user.getId(), user);
}
public static Map<Integer, User> getUsersMap() {
return usersMap;
//return Collections.unmodifiableMap(usersMap);
}
}
Then we have the UsersCreator that creates those users and stores them in a map:
public class UsersCreator {
public static void createUsers() {
UsersHolder.addUser(new User(1, "Peter"));
System.out.println("Created user " + UsersHolder.getUsersMap().get(1).getName());
UsersHolder.addUser(new User(2, "Paul"));
System.out.println("Created user " + UsersHolder.getUsersMap().get(2).getName());
}
public static void main(String[] args) {
UsersCreator.createUsers();
System.out.println("Number of users before notification: " + UsersHolder.getUsersMap().size());
new UsersNotificator().notifyAllUsersButPeters(UsersHolder.getUsersMap());
System.out.println("Number of users after notification: " + UsersHolder.getUsersMap().size());
}
}
And the notificator that notifies all but Peters:
public class UsersNotificator {
public void notifyAllUsersButPeters(final Map<Integer, User> map) {
//we don't need peters, so we'll remove them from the list;
Iterator<Entry<Integer, User>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
if (iterator.next().getValue().getName().equals("Peter")) {
iterator.remove();
}
}
//now we can notify all from the list;
notifyUsers(UsersHolder.getUsersMap());
}
private void notifyUsers(Map<Integer, User> map) {
for (final User user : map.values())
System.out.println("notifyingUsers: " + user.getName());
}
}
Now - the notificator was presented with a map and it may modify it, which it does. It doesn't know that it shouldn't modify it as it's global usersMap. In effect it removes all users with name Peter. It does it for it's own purposes, but the results will be visible for every other class using UsersHolder.
The result is as follows:
Created user Peter
Created user Paul
Number of users before notification: 2
notifyingUsers: Paul
Number of users after notification: 1
When returning unmodifiableMap in UsersHolder the removal will not be possible. The only way would be to create new map with users to notify, so our usersHolder is safe.
This example is a bit big, sorry for that, i failed to think of/create somehting shorter.
Unmodifiable map helps to keep your classes Immutable which is safe(as presented in the example) especially in multithreaded enviroment.
There are many situations in which you do not want your collection to be modifiable. Whenever you know that the collection is initialized with exactly the content it should contain at all times, it can provide security to make it unmodifiable.
The (rather long) example provided by another user is a good example of where it often causes problems. Whenever you traverse a collection, there is a risk you change the collection if you forget to do it on a copy. Making the collection unmodifiable catches and prevents this easy to make mistake.
I have a ListView full of POJOs and want a label in the GUI to display informations from the selected item.
My POJO looks something like that:
class Customer {
private String name;
...
public String getName() {
return name;
}
Now when the user selects a customer from the list I want the name of the selected customer displayed in a label.
Obviously I can't bind to the name directly because it is not a Property. (And I don't want to replace my Customers Strings with StringProperty-objects because the SimpleStringProperty is not serializable and I need the Customer to be transfered via RMI.)
I've tried the BeanPathAdapter from JFXtras (which looks really nice by the way) like this:
BeanPathAdapter<MultipleSelectionModel> customerBeanPathAdapter;
customerBeanPathAdapter = new BeanPathAdapter<>(lstCustomers.getSelectionModel());
customerBeanPathAdapter.bindBidirectional("selectedItem.name", lblCustomerName.textProperty());
But this solution only throws me an Exception:
...
Caused by: java.lang.IllegalArgumentException: Unable to resolve accessor getSelectedItem
at jfxtras.labs.scene.control.BeanPathAdapter$FieldHandle.buildAccessor(BeanPathAdapter.java:3062)
at jfxtras.labs.scene.control.BeanPathAdapter$FieldHandle.buildAccessorWithLikelyPrefixes(BeanPathAdapter.java:3022)
at jfxtras.labs.scene.control.BeanPathAdapter$FieldHandle.updateMethodHandles(BeanPathAdapter.java:2986)
at jfxtras.labs.scene.control.BeanPathAdapter$FieldHandle.<init>(BeanPathAdapter.java:2977)
at jfxtras.labs.scene.control.BeanPathAdapter$FieldBean.performOperation(BeanPathAdapter.java:1348)
at jfxtras.labs.scene.control.BeanPathAdapter$FieldBean.performOperation(BeanPathAdapter.java:1186)
at jfxtras.labs.scene.control.BeanPathAdapter.bindBidirectional(BeanPathAdapter.java:567)
at jfxtras.labs.scene.control.BeanPathAdapter.bindBidirectional(BeanPathAdapter.java:369)
at at.gs1.sync.qm.client.gui.MainWindowController.initialize(MainWindowController.java:61)
... 22 more
Caused by: java.lang.IllegalAccessException: symbolic reference class is not public: class javafx.scene.control.ListView$ListViewBitSetSelectionModel, from jfxtras.labs.scene.control.BeanPathAdapter$FieldHandle
at java.lang.invoke.MemberName.makeAccessException(MemberName.java:512)
at java.lang.invoke.MethodHandles$Lookup.checkSymbolicClass(MethodHandles.java:1113)
at java.lang.invoke.MethodHandles$Lookup.resolveOrFail(MethodHandles.java:1094)
at java.lang.invoke.MethodHandles$Lookup.findVirtual(MethodHandles.java:626)
at jfxtras.labs.scene.control.BeanPathAdapter$FieldHandle.buildAccessor(BeanPathAdapter.java:3049)
... 30 more
So I hoped there would be a better solution than to use lstCustomers.getSelectionModel().selectedItemProperty().addListener(...) and handle the population of the labels there manually.
A better solution I think, to the one I gave before, is to use the BeanPathAdapter as you tried.
However the BeanPathAdapter needs to have the following property added to it:
private final ObjectProperty<B> beanProp = new SimpleObjectProperty<>();
{
beanProp.addListener( new ChangeListener<B>()
{
#Override
public void changed( ObservableValue<? extends B> ob, B oldVal, B newVal )
{
setBean( newVal );
}
} );
}
public ObjectProperty<B> beanProperty()
{
return beanProp;
}
Then in your code you need the following:
BeanPathAdapter<Customer> custBean;
custBean = new BeanPathAdapter<>( new Customer() ); // empty or any customer
custBean.bindBidirectional( "name", label.textProperty() );
custBean.beanProperty().bind( listview.getSelectionModel().selectedItemProperty() );
I don't think that there's a simple one liner that you are looking for.
You could do the following:
label.textProperty().bind( Bindings.selectString( listview.getSelectionModel().selectedItemProperty(), "name" ) );
But you will need to modify your Customer POJO like so:
class Customer
{
private String name;
...
public String getName() { return name; }
public ReadOnlyStringProperty nameProperty()
{
return new SimpleStringProperty( name );
}
}
I don't think this is recommended though because properties are expected to reflect changes in the underlying data and the above will only reflect the name as it was when nameProperty is called. So if setName is called the property won't reflect the change. If the Customer name never changes then you could get away with this.
So, I have already created an ArrayList<>, say Staff List.
private List<Staff> staffs = new ArrayList<Staff>();
public StaffFacade() {
staffs.add(new Staff("1", "James"));
staffs.add(new Staff("2", "Mike"));
staffs.add(new Staff("3", "Lina"));
staffs.add(new Staff("4", "John"));
}
public List<Staff> getStaffs() {
return staffs;
}
And I want to create another List that contains Staff List (by adding), so that I don't have to add the same Staff in StaffFacade?
I already created this BorrowFacade:
private List<Borrow> borrows = new ArrayList<Borrow>();
public BorrowFacade() {
borrows.add(new Borrow()) //How do I add this?
}
public List<Borrow> getBorrows() {
return borrows;
}
Referring to my question above, I don't know how to add new Staff List that has already been created.
This is the constructor for the Borrow List:
public Borrow(Date dueDate, Staff staff, Book books) {
this.dueDate = dueDate;
this.staff = staff;
this.books = books;
}
Of course, I put Date there because I wanted to add Date inside the List too.
MAJOR EDIT
Okay so let me try to put it this way. I have 4 classes which is StaffFacade, BorrowFacade, Borrow and Staff.
This is what I wrote inside StaffFacade:
public class StaffFacade {
private List<Staff> staffs = new ArrayList<Staff>();
public StaffFacade() {
staffs.add(new Staff("1", "James"));
staffs.add(new Staff("2", "Mike"));
staffs.add(new Staff("3", "Lina"));
staffs.add(new Staff("4", "John"));
}
public List<Staff> getStaffs() {
return staffs;
}
}
BorrowFacade:
public class BorrowFacade {
private List<Borrow> borrows = new ArrayList<Borrow>();
public BorrowFacade() {
borrows.add(staffsList);
}
public List<Borrow> getBorrows() {
return borrows;
}
}
Borrow (parts of it, the rest are just setters and getters)
public class Borrow {
String id;
Date dueDate;
Staff staff;
Book books;
public Borrow(String id, Date dueDate, Staff staff, Book books) {
this.id = id;
this.dueDate = dueDate;
this.staff = staff;
this.books = books;
}
Staff:
public class Staff{
String id, name;
public Staff(String id, String name) {
this.id = id;
this.name = name;
}
The problem is in BorrowFacade. I don't know how to add List that has been created in StaffFacade into BorrowFacade's List which is List<Borrow> borrows;
I'm very sorry for the confusion. If anything please ask me. I really want this program to work.
If you have a collection you can addAll(someOtherCollection); but I am not sure I fully understand your question: you refer to a 'constructor for the Borrow List' but you show a constructor for a Borrow class which is not a list.
You seem to be mixing up an instance of an individual class (e.g. Book) with a collection or plurality of that class: Book books (why is it plural? What are you trying to express?)
Edit:
Based on you comment, I think you're trying to understand how to construct the Borrow objects to be placed in the list.
The difference between constructed the Staff List is that you 'know' the staff ahead of time - albeit these are canned values.
The Borrow object seems to express a particular person borrowing a particular book due back on a certain date. If so, you need to have these details somewhere, for example from a database. The reason you're having trouble is you're trying to construct these objects in your Facade instead of just encapsulating ones that already exist.
public class Facade {
private List<Borrow> borrows = new ArrayList<Borrow>();
// Pass the items in to the constructor
public Facade(List<Borrow> borrows) {
this.borrows.addAll(borrows);
}
// You could call this externally in a loop to populate one by one
public void addBorrow(Borrow borrow) {
borrows.add(borrow);
}
}
To restate: your Staff and your Borrow objects have to come from somewhere, so if they're already in a collection, use addAll, if not, just iterate the list and call add. Don't construct the objects in your Facades.
Edit 2:
In response to your amended question, you can't do this. You're trying to add a list of a particulr object (Staff) in to a list of another type of object (Borrow). This is just inherently wrong. I don't quite know how else to say it. If you asked me to give you a List of my favourite Stack Overflow questions, would you expect to find my favourite Stack Overflow user in that list? This is the fundamental nature of type safety. Now, if you asked me to give you a list of my Favourite Things then it is perfectly reasonable to expect to find various types of things in there - Stack Overflow Questions, Wines, Foods, etc. because they would conceptually share a common Favourite parent class or interface.
To be frank, I think you neeed to (re-)read up on the basic nature of Java generics and type safety, but in pursuit of the almighty reputation, here goes:
Note: I'm using StaffMember and BorrowedItem as names here to try to illustrate the value of good naming conventions.
You seem to want a Facade class for reasons none of us understand. Okay, we can accept that. Your Facade class seems to contain a list of objects. You have created multiple classes to accomplish this, with no discernable difference between the two except which objects are listed inside. Generics to the rescue:
public class Facade<T> {
private List<T> list = new ArrayList<T>();
public Facade(List<T> existingList) {
list.addAll(existingList);
}
}
This facade holds a list of objects, meaning you can do this:
List<StaffMember> staffMembers= new ArrayList<StaffMember>();
// .. assume list is populated here
Facade<StaffMember> staffMembersFacade = new Facade<StaffMember>(staffMembers);
Likewise, with the same facade class:
List<BorrowedItem> borrowedItems = new ArrayList<BorrowedItem>();
// ... populate borrowed items
Facade<BorrowedItem> borrowedItemsFacade = new Facade<BorrowedItem<(borrowedItems);
But you aren't adding StaffMember objects to the borrowedItemsFacade. At least not directly - in your example a BorrowedItem has a Date and it also points to which StaffMember borrowed it.
So at this point you have two lists - a list of StaffMembers, and a list of BorrowedItems but you really have to ask yourself what purpose does this serve? Doesn't it make more sense for a single StaffMember to have a List<BorrowedItem> to keep track of all the items they borrowed?
class BorrowedItem {
Date dueDate;
StaffMember borrower;
}
class StaffMember {
String name;
List<BorrowedItem> borrowedItems;
}
Now this provides the opportunity to add a function to the StaffMember like this:
List<BorrowedItem> getOverdueItems() {
List<BorrowedItem> overdueItems = new ArrayList<BorrowedItem>();
Date today = getTodaysDate(); // Calendar.getInstance etc.
for (BorrowedItem borrowedItem : borrowedItems) {
Date dueDate = borrowedItem.getDueDate();
if (today.after(dueDate)) {
overdueItems.add(borrowedItem);
}
}
return overdueItems;
}
Do you see how you need to create meaningful relationships between these classes in order for there to be anything useful to happen?
Then you can add functions to let someone lend an item to another person, or take something from someone, etc.
So yeah, Collections.addAll is what you're looking for, I think.