Home » Android » android – Java- Best practice for getters, single getter or multiple for different variables?

android – Java- Best practice for getters, single getter or multiple for different variables?

Posted by: admin June 15, 2020 Leave a comment

Questions:

I am relatively new to Android programming (~ 2 Months)
Is it necessary to have getters for dozens of different variables?

For example –

//Yes I realise that this isn't 'dozens'
public float getX() {
    return position.x;
}

public float getY() {
    return position.y;
}

public float getWidth() {
    return width;
}

public float getHeight() {
    return height;
}

public float getRotation() {
    return rotation;
}

While it would be necessary to have different getters and setters for floats and strings for example, is it bad practise, and if so why is it, to use something like a switch statement to return different variables?

public float returnSomething(String theThing) {
    switch (theThing) {
        case "height":
            return height;
        case "rotation" :
            return  rotation;
        case "width":
            return  width;

        default:
            return 0;
    }
}

So, is the above code considered bad? If so, please explain why.

Thanks for any help, this isn’t really a problem as either way works fine, I just don’t see why people would use dozens of getters and setters if there isn’t a good reason.

I suppose the same question applies to setters

How to&Answers:

Because the moment you do something like

public float returnSomething(String theThing) {
    switch (theThing) {
        case "height":
            return height;
        case "rotation" :
            return  rotation;
        case "width":
            return  width;

        default:
            return 0;
    }
}

I can feel the next question for Stack Overflow, “Why is my height always 0?”

And then post code like

public class GameThingy {
      //...

     private void doStuff(GameObject gameObject) {
         float gravity = 5*gameObject.returnSomething("hieght");
         gameObject.setSomething("velocyty", gravity+50);
     }
}

Technically the moment you make a typo anywhere, you’ll have issues finding the source of the problem. That, and you’re lucky that the fields are all float, they don’t have to be.

Edit: By the way, this is actually a typical problem in defining the field of interest in some database queries. As in, having to specify the field you’re looking for by a String.

A real-life example is RealmQuery<T>.

A RealmQuery<T> looks like this:

RealmQuery<User> query = realm.where(User.class);

// Add query conditions:
query.equalTo("name", "John");

// Execute the query:
RealmResults<User> result1 = query.findAll();

Where they assume the class User is something like this:

public class User {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

An interesting note is that Realm creates a “proxy subclass” where they redefine the setName and getName methods (aka, you need getters/setters to make some systems work! They assume you have them!)

But what’s important here is that you need to provide the field name with "name".

Later on, anyone can make a typo, or you just won’t remember the fields off the top of your head. In this particular case, they tend to create a metamodel (something that stores the name of the fields for you) for the purpose that you won’t have to use Strings to refer to field names.

For example, in Criteria API, instead of

Root<Pet> pet = cq.from(Pet.class);
cq.select(pet.get("name"));

They have the metamodel like this:

Root<Pet> pet = cq.from(Pet.class);
cq.select(pet.get(Pet_.name));

Thus, it eliminates the need for Strings.

And similarly, what I tend to do with Realm is that I create a “metamodel” (although you can now have it generated for you automatically with RealmFieldNamesHelper):

public class User
        extends RealmObject {
    @PrimaryKey
    private long id;
    private String name;

    public static enum Fields { //this is the "metamodel"
        ID("id"),
        NAME("name");

        private String fieldName;

        Fields(String fieldName) {
            this.fieldName = fieldName;
        }

        public String getField() {
            return fieldName;
        }

        @Override
        public String toString() {
            return getField();
        }
    }
}

And thus you can replace the query like so

RealmResults<User> result2 = realm.where(User.class)
                                  .equalTo(User.Fields.NAME.getField(), "John")
                                  .or()
                                  .equalTo(User.Fields.NAME.getField(), "Peter")
                                  .findAll();

But with getters and setters, you already have the type safety, and you already have the methods that you don’t have to remember off the top of your head – and so, you have the compilation time error checks.

So to answer your question, the reason why it’s a bad practice to refer to variables by string name in Java is because

1.) typos can happen, and the error occurs only in runtime, not compile time

2.) it is not type-safe; you are lucky that you get floats back no matter what in this example, but the moment it can be a String, you need to return Object and cast it or use public <T> T get(String field) { ... } and whoever calls the method must know exactly what they are receiving – also runtime-error prone.

Answer:

Getters & Setters give you type safety. However use it only for variables that you need to access (get/set) from outside the class. You can and should minimize it by using suitable constructors, which for most cases is the right way to go because it increases clarity of the code by eliminating magical changes in the background, which can be a serious pain for multi-threaded code but can be a big issues even in regular code with poor programming.

Just because you have twenty class level private variables doesn’t mean you have to create getXXX() & setXXX() for each!

BTW: Most IDE’s these days like Eclipse can automatically generate them for you.

Answer:

Just create getters and setters when you need them.

public float returnSomething(String theThing) {
    switch (theThing) {
        case "height":
            return height;
        case "rotation" :
            return  rotation;
        case "width":
            return  width;

        default:
            return 0;
    }
}

If you try to do area = returnSomething("width") * returnSomething("heigth") you will allways end up with 0 area. Why, notice the spelling mistake. If you have separate methods, then the compiler will say it at compile time.

Also, you need to do a lot of checks if you have many variables.

Answer:

I am no friend of getters/setters either, but they are probably the best solution here. The IDE will provide code generation for such fields.

Reason: rock solid, not error prone, easier for later changes.

Make only getters/setters when they are really needed. A public void move(float, float) and other more interesting methods may place the coordinate calculation largely inside the class itself.

Answer:

You only need to declare getters and setters for the variables that you will need to use or update.

I think it’s just a habit, your returnSomething will work fine, but it’s easier to call and understand:

class.getHeight()

than

class.returnSomething("height")

Answer:

Interesting topic. I feel there is a third answer. There exists an argument that says that Getters and Setters are evil, and you should avoid using them at all. Because you are essentially declaring members as private but granting public modification access through getters and setters.

Let us compare the two. Here is a person class using getters and setters

public class Person {
    private String name;
    private int age;

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return this.age;
    }

    public void setAge(int name) {
        this.age = age;
    }
}

Using this class:

Person me = new Person();
me.setName("Ian");
me.setAge(24);

System.out.println("Name: " + me.getName());
System.out.println("Age: " + me.getAge());

Let us now create the same class without using getters and setters.

public class Person {
    public String name;
    public int age;
}

and access will be:

Person me = new Person();
me.name = "Ian";
me.age = 24;

System.out.println("Name: " + me.name);
System.out.println("Age: " + me.age);

As you can see the getter and setter pattern adds quite a bit of extra typing. There is however a great deal of advantages of using getters and setters.

The whole argument against the use of getters and setters in an Object oriented language can be summarized as:

Procedural code gets information then makes decisions. Object-oriented
code tells objects to do things. — Alec Sharp

As a simple example, say we want to write the information of a Person to the database. What some developers tend to do is to create a database interface that has a function

void writeToPersons(Person person)

which will use getters and setters to write the required data to the database.

According to the getter and setter nay sayers, Person itself should be responsible for writing itself the database.

public class Person implements Storable {

    public void writeToDatabase() {

    }
}

To answer your question, I would rather use getters and setters than your second suggestion, because you will eventually add a set of constants or enums for each property. This requires code maintenance in two different places, which simply adds another point of failure.

Answer:

I agree with the previous answers, but I’m going to generalize a little. The problem with the returnSomething approach is that it shifts some spelling checks from compile time to run time.

There are times and places for very flexible code in which most checks are done at run time. In those situations, I don’t use Java.

The big advantages to compile time checks, and to languages that support them, is that they do the equivalent of universally quantified tests: “For every compilable program that does not use reflection, every get request is for a valid attribute.” rather than “For all the test cases in this test suite, every get request is for a valid attribute”.