While using Sqlite as DB in android, Many of us create a class like DbConstants where we save all the Table names, column names as constants.
In case of Realm DB, we prepare POJO classes which are represented as tables and fields as column names respectively.
Is there any way though which i can avoid creating another constants file here ?
Use Case :
POJO representing User table :
public class User extends RealmObject {
private String name;
private int age;
#Ignore
private int sessionId;
// Standard getters & setters generated by your IDE…
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public int getSessionId() { return sessionId; }
public void setSessionId(int sessionId) { this.sessionId = sessionId;
}
}
so while we query User table as follows :
RealmResults<User> result = realm.where(User.class)
.equalTo("name", "John")
.or()
.equalTo("name", "Peter")
.findAll();
I don`t want to use the literals like “name” here. So any other elegant solution or best practice ?
The usual practice is having a static final constant in your model class like this:
public class User extends RealmObject {
public static final String NAME = "name";
public static final String AGE = "age";
// Fields, constructors, getters, setters,
}
realm.where(Person.class).equalTo(Person.NAME, "John").findAll();
If you want an automated way of doing it you can take a look at: https://github.com/cmelchior/realmfieldnameshelper
Related
I am supposed to refactor duplicated attributes in Student class. I have Student and Professor classes as below. I am really confused about how to do refactoring with attributes. Should I add a new class, or made modifications in one of the classes. If so, how? I could not understand how to proceed with this to-do.
private final String matrNr;
private final String name;
private final int age;
private int semester;
private final String email;
public Student(String name, int age, String email, String matrNr, int semester) {
this.matrNr = matrNr;
this.name = name;
this.age = age;
this.semester = semester;
this.email = email;
}
public String getEmail() {
return email;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public int getSemester() {
return semester;
}
public String getMatrNr() {
return matrNr;
}
public void increaseSemester(){
semester = semester + 1;
}
}
And the professor is a like:
private final String persNr;
private final String name;
private final int age;
private final String email;
public Professor(String name, int age, String email, String persNr) {
this.persNr = persNr;
this.name = name;
this.age = age;
this.email = email;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
public String getEmail() {
return email;
}
public String getPersNr() {
return persNr;
}
}
Thanks for any kind of helps!
Your goal is to refactor duplicated attributes in the Student and Professor classes. The way to do this is to create a parent class which defines the common attributes (like "name"), and modify Student and Professor classes to extend the common parent class. In this way, both Students and Professors can have a "name", even though you have defined "name" only once in the common parent.
Below shows how you could do this with a common "Human" parent class, how the constructors would work, and how you could define a Student-only attribute (semester).
Here is a simple version a common Human class:
common "Human" class
each Human has a "name"
the name is set in the constructor (so when you're creating an object) and cannot be changed later ("name" is final; also no "setHuman()")
class Human {
private final String name;
public Human(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
Here's a simple Professor class:
by definition, a Professor is a Human (Professor extends Human)
when creating a Professor, you must specify the "name" (which is then passed to the Human constructor)
once you have a Professor, you can call getName() (which is defined on the Human class)
class Professor extends Human {
public Professor(String name) {
super(name);
}
}
Here's a simple Student class:
Student is a little different - in addition to a name, it also has a "semester"
when creating a Student, the constructor requires a name and semester, and the Student class itself keeps track of "semester" – so it's fine to have semester defined on Student, and name defined on Human.
you can call getName() (defined on Human)
you can call getSemester() (defined on Student)
class Student extends Human {
private final int semester;
public Student(String name, int semester) {
super(name);
this.semester = semester;
}
public int getSemester() {
return semester;
}
}
I have a User class which saves some extra data on the user. This data is stored in/coming from Firestore. I have a couple of fields which are working(name, surname, lastLogin) but a couple of them are not working(blocked).
When I make the field public they work, but when I try to use a setter, it doesn't. I tried cleaning the build and rebuilding it. I know it is not saving the field due to #Exclude, that is intended.
What am I doing wrong? The field type doesn't matter, I've added a new String field which gave the same warning, while name and surname work.
The database:
**userid**
{
"name" : "John",
"surname" : "Doe",
"lastLogin" : **timestamp**,
"blocked" : true
}
The class:
#Keep
public class User
{
private String name;
private String surname;
private Date lastLogin;
private boolean blocked = false;
public User()
{
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getSurname()
{
return surname;
}
public void setSurname(String surname)
{
this.surname = surname;
}
public Date getLastLogin()
{
return lastLogin;
}
public void setLastLogin(Date lastLogin)
{
this.lastLogin = lastLogin;
}
#Exclude
public boolean isBlocked()
{
return blocked;
}
public void setBlocked(boolean blocked)
{
this.blocked = blocked;
}
The problem in your code is that the constructor in the User class is private. That's not the correct way in which you should create a new instance of the class. JavaBeans require a no-argument constructor to be present.
When Cloud Firestore SDK deserializes objects that are coming from the database, it requires that any objects in use, to have this public no-argument constructor, so it can use it to instantiate the object. Fields in the objects are set by using public setter methods or direct access to public members, as you already tried.
Because your constructor is private, the SDK doesn't really know how to create an instance of it. So it is mandatory to change it as public. A correct way to create that class should be:
class User {
private String name;
private String surname;
private long lastLogin;
private boolean blocked = false;
public User() {} //Needed for Cloud Firestore
public User(String name, String surname, long lastLogin, boolean blocked) {
this.name = name;
this.surname = surname;
this.lastLogin = lastLogin;
this.blocked = blocked;
}
//Getters and setters are not mandatory
}
Also please note that the setters and the getters are not required. Setters are always optional because if there is no setter for a JSON property, the Firebase client will set the value directly onto the field.
Edit:
According to your comment:
but it does not explain why some fields are working and others aren't. It should not work at all, right?
Yes, that's right, all should work. The reason why some of them are not working is that the blocked property in your User class is of type boolean while in your database is of type String and this is not correct. Both types must match.
And the private constructor is due to the singleton instance, as far as I know, the constructor should be private to avoid creating new instances of the class.
No, the constructor must be public. I think there is a misunderstanding. Every time you use FirebaseDatabase.getInstance(), a single socket connection between your application and the Firebase servers is opened. From that moment on, all traffic between the application and the database goes over the same socket. So it doesn't matter how many times you create an instance, it will always be a single connection. Regarding your POJO class, there is no need for such a Singleton because Firebase always needs to know how to create an instance of that class, using the public no-argument constructor.
Try to create a constructor with parameters for all class attributes along with a non-parameter constructor and then in the java class where you store in firebase, create object from user and pass it.
for example:
package com.example.spacing.Model;
public class User {
private String username;
private String phone;
private String id;
private String imageURL;
private String email;
public User(String username, String email ,String phone, String id, String imageURL) {
this.username = username;
this.email=email;
this.phone = phone;
this.id = id;
this.imageURL = imageURL;
}
public String getImageURL() {
return imageURL;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public User() {
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
and
FirebaseDatabase.getInstance().getReference("Users")
.child(FirebaseAuth.getInstance().getCurrentUser().getUid())
.setValue(user);
You can try to add #field:JvmField to your boolean variable inside your User class.
How can we create constructor, to set different values that are from same data type?
Its impossible two create two identical constructors:
public User(int age, String name){
}
public User(int age, String surname){
}
User user1 = new User(33, Jack);
User user2 = new User(33, Sparrow);
Since both name and surname are from same data type, its impossible to know which value the user has meant to set.
We can create a constructor that has all the properties and then pass null for unset arguments.
public User(int age, String name, String surname){
}
User user1 = new User(33, Jack);
User user2 = new User(33, null, Sparrow);
There is a way to make it, using HashMap imitating the javascript object literal.
package javaapplication;
import java.awt.Color;
import java.util.HashMap;
public class JavaApplication {
public static class User {
public int age;
public String name;
public String surname;
public Color hairColor;
public User(HashMap<String, Object> arguments) {
if (arguments.containsKey("hairColor")) {
this.hairColor = (Color) arguments.get("hairColor");
}
if (arguments.containsKey("name")) {
this.name = (String) arguments.get("name");
}
if (arguments.containsKey("surname")) {
this.surname = (String) arguments.get("surname");
}
if (arguments.containsKey("age")) {
this.age = (int) arguments.get("age");
}
}
}
public static void main(String[] args) {
User jack1 = new User(new HashMap<String, Object>() {
{
put("hairColor", Color.RED);
put("name", "Jack");
put("age", 33);
}
});
System.out.println(jack1.hairColor); // java.awt.Color[r=255,g=0,b=0]
System.out.println(jack1.surname); // null
System.out.println(jack1.name); // Jack
System.out.println(jack1.age); // 33
User jack2 = new User(new HashMap<String, Object>() {
{
put("hairColor", Color.RED);
put("surname", "Sparrow");
put("age", 33);
}
});
System.out.println(jack2.hairColor); // java.awt.Color[r=255,g=0,b=0]
System.out.println(jack2.surname); // Sparrow
System.out.println(jack2.name); // null
System.out.println(jack2.age); // 33
}
}
Is there a more elegant way to do it?
The typical ways of doing this are with static creation methods or a builder object.
Static creation methods are the way to go if there are distinctive patterns of usage. For your example (where this perhaps isn't the case).
public static User ageName(int age, String name) {
...
}
public static User ageSurname(int age, String surname) {
...
}
...
In general it's a good idea to avoid any overloading. With constructors Java rather forces you into it, but sometimes you have to break free.
Builder may go something like:
public class UserBuilder { // This could be made a nested class of User
private int age = -1;
private String name;
private String surname;
private Color hairColor;
public UserBuilder age(int age) {
this.age = age;
return this;
}
// ... other properties ...
public User build() {
// Or return new User(this)
return new User(age, name, surname, hairColor);
}
}
This uses the typical Java dirty hack of returning this in a builder so you can build a train wreck.
User user1 = new UserBuilder().age(33).name("Jack").build();
You really need real examples of usage to see how it should fit in.
You are over-complicating things by using a HashMap here. Instead you could make use of the Builder Pattern for the same.
class User {
// required parameters
private int age;
// optional parameters
private String name;
private String surname;
public int getAge() {
return age;
}
public String getName() {
return name;
}
public String getSurname() {
return surname;
}
private User(UserBuilder userBuilder) {
this.age = userBuilder.age;
this.name = userBuilder.name;
this.surname = userBuilder.surname;
}
public static class UserBuilder {
// required parameters
private int age;
// optional parameters
private String name;
private String surname;
public UserBuilder setName(String name) {
this.name = name;
return this;
}
public UserBuilder setSurname(String surname) {
this.surname = surname;
return this;
}
public UserBuilder(int age) {
this.age = age;
}
public User build() {
return new User(this);
}
}
}
Now you can use it :
User user1 = new User.UserBuilder(12).setName("Jack").build();
User user2 = new User.UserBuilder(12).setSurname("Sparrow").build();
This assumes that age is a mandatory field, name and surname are optional fields for creating an object of User. You can tweak it as per your requirement accordingly.
first time I've tried this. I need to be able to replace an email for subclass Student and sublass Teacher after an email has been inputted, I have a parent class and superclass which is where I believe I need to add my changeEmail method. I may be a way off here but can I use stringBuilder or is there an easier way? Real noob when it comes to this.
SUBCLASS -
public class Teacher extends Member
{
private String qualifications;
public Teacher(String name, String email, String qualifications)
{
super(name, email);
this.qualifications = qualifications;
}
public String getQualifications()
{
return qualifications;
}
public String toString()
{
StringBuffer details = new StringBuffer();
details.append(super.getName());
details.append(' ');
if(qualifications != null && qualifications.trim().length() > 0) {
details.append("(" + qualifications + ")");
details.append(' ');
}
details.append(super.getEmail());
return details.toString();
}
}
SUBCLASS -
public class Student extends Member
{
private int attendance;
public Student(String name, String email)
{
super(name, email);
this.attendance = 0;
}
public int getAttendance()
{
return attendance;
}
public void markAttendance(int attendance)
{
this.attendance += attendance;
}
public void print()
{
System.out.println(super.getName() + " (" + attendance + ")");
}
}
SUPERCLASS -
public class Member
{
private String email;
private String name;
public Member(String name, String email)
{
this.name = name;
this.email = email;
}
public String getEmail()
{
return email;
}
public String getName()
{
return name;
}
public String changeEmail()
{
//..........
}
}
Since changeEmail is a public method in the superclass, the subclasses can access it too. Student (as well as Teacher) is a Member.
public String changeEmail(String newEmailAddress) {
String old = email;
this.email = newEmailAddress;
return old;
}
What I changed was adding a parameter (String newEmailAddress) and then set the new value to the email instance field.
(EDIT: I updated the answer to return the old email address. I don't know why a method like this would return anything but anyways..)
That is called inheritance, basically if you have some shared variables, you can use some parent class and with the keyword extends create some subclasses.
All subclasses, which inherits the parent class, can have their own class variables, but also are having the parent variables.
In your case you can image the diagram like that- obvious, doesnt?
So...
Parent class member is having these class variables:
- String : mail
- String : name
You have two subclasses- Student and Teacher:
Teacher class variables:
qualifications
mail, name (inherited from parent!)
Student class variables:
attendance
mail, name (inherited from parent!)
Notice- with the keyword super you are calling the constructor (or simply "class" other methods) from the parent, so in Teacher and Student class, you will call exactly following:
public Member(String name, String email) {
this.name = name;
this.email = email;
}
To be able change the email, you need following
1) implement methods in parent class
2) optional- add call to child classes, and for usage outside the class also add some external method (without this you can still use public parent class methods)
Eg.
in parent
public void changeEmail(String newEmail) {
this.email = newEmail;
}
public String changeEmailWithReturnOld(String newEmail) {
String oldMail = this.email;
changeEmail(newEmail); //calling above
return oldMail;
}
In childs
public String changeTheMailWithReturnOld(String newMail) {
return super.changeEmailWithReturnOld(newMail); //super means super class, parent
}
Clear? :)
Then you can call following:
Teacher teacher1 = new Teacher("foo", "foo#foo.foo", "whateverFoo");
teacher1.changeEmail("someNewFoo#foo.foo"); //parent method
teacher1.changeEmailWithReturnOld("someNewFoo#foo.foo"); //Child method
I'm writing my new java project and a requirement is to represent product that can belong to a category.
I'm using a database in my project and I connect products and categories by a foreign key.
In the code, instead, I've to use SOLID design and I don't understand how can I connect products and categories.
In a first version, the code was
public class Product {
private int ID;
private String name;
private String descr;
private int stock;
private float price;
private int category;
public Product(int anID, String aName, String aDescr, int aStock, float aPrice, int aCategory) {
this.ID = anID;
this.name = aName;
this.descr = aDescr;
this.stock = aStock;
this.price = aPrice;
this.category = aCategory;
}
public int getID() { return this.ID; }
public String getName() { return this.name; }
public String getDescr() { return this.descr; }
public int getStock() { return this.stock; }
public float getPrice() { return this.price; }
public int getCategory() { return this.category; }
public void decreaseStock(int x) { this.stock -= x; }
}
and
public class Category {
private int ID;
private String name;
private String descr;
public Category (int anID, String aName, String aDescr) {
this.ID = anID;
this.name = aName;
this.descr = aDescr;
}
public int getID() { return this.ID; }
public String getName() { return this.name; }
public String getDescr() { return this.descr; }
}
... but I'm thinking that product can implements category, in order to have all information in one object and not jump between two classes...
Which is the best way to write it?
You should not mimic the underlying database table structure in your Java classes verbatim. The correct way to do and which every ORM approach that I worked on until now uses is as follows:
Product class stores a reference to a Category instance.
When fetching the records from a database within the data access layer you would explicitly write code to to create a Category object first and then pass it to the Product class constructor when creating the Product object.
This way the Java class hierarchy reflects the true business relationship between a Product and its related Category. This also has the advantage of abstracting the storage details from the application - consider what would happen with the approach you are currently adopting if the data were to be stored in a NoSQL database. However, by adopting the approach presented in this answer you would only need to change the Data access layers for creating the correct objects - your class design remains intact (The O of Open-Closed principle in SOLID).