8

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

Unihedron
  • 10,902
  • 13
  • 62
  • 72
Theo Pearson-Bray
  • 775
  • 1
  • 13
  • 32
  • The default case you are forced to add in the second example illustrates the weakness of the second method. You are giving up a degree of safety by allowing yourself to pass in any string. Perhaps if you were to provide a dimension enum, with values like `HEIGHT`, `ROTATION`, `WIDTH` etc., you could achieve functionality similar to the second example but with added type-safety. (Alternatively, you could use the @StringDef annotation...) – stkent Jul 20 '15 at 13:31
  • Using a getter directly would save a `switch` statement (any time you want to get a value, you need to switch over all cases) – Hacketo Jul 20 '15 at 13:31
  • I will never use switch because , If I have Integer ,Float ,Strings and boolean variables I still have to setup 4 different function and specify all the cases + there can be a chance I send wrong string( String theThing) and no match was found, Second reason Eclipse and Android Studio generate getter and setter for me so I can focus on my logic not on getter and setter – Sanjeev Jul 20 '15 at 13:33
  • 1
    If you want the type-safety of **statically typed** languages, any expression, whose return type is only available during runtime (such as your `switch`-based approach) is counterproductive. – Sebastian S Jul 20 '15 at 13:35
  • So, it is bad practice. Thanks for your replies. Is there no down side to having so many different getter methods? I mean, with a particularly large application would there not be hundreds of getters and setters? As I said, I am new to this, but would that not increase the compile time? Or is it simply not significant in comparison to the safety provided by using individual methods? – Theo Pearson-Bray Jul 20 '15 at 13:37
  • You only need a getter if the variable will be used outside the class it is defined in. But be careful, don't just make everything because it makes things easy. it will also lead to "spaghetti code", bugs and hard to maintain code. Learn about OOP and how to design classes properly. – Simon Jul 20 '15 at 20:50

7 Answers7

9

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.

EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428
  • That makes sense. I assumed that there would be a reason, seeing as this is how more experienced programmers seem to be doing it. – Theo Pearson-Bray Jul 20 '15 at 13:42
  • I added some more data just now if you're curious about a real example. – EpicPandaForce Jul 20 '15 at 13:49
  • @EpicPandaForce Your answer is good but next time don't use such a technical example to explain a point I cannot understand anything after the word EDIT – Sanjeev Jul 20 '15 at 13:54
  • @TheoPearson-Bray this is actually a valid question by the way - because on the contrary, *Javascript* is not a statically typed language. You don't specify `int something = 5;`, you say `var something = 5;`. Just like how in Javascript, instead of something `Runnable runnable = new Runnable() { public void run() { System.out.println("hello!"); } }` you would have `var runnable = function() { console.log("hello"); };` It doesn't really care about types until you try calling on them. And then you'll run into runtime errors, unless you have a really good IDE that checks for errors for you. – EpicPandaForce Jul 20 '15 at 14:13
3

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.

2

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.

Viktor Mellgren
  • 4,318
  • 3
  • 42
  • 75
2

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.

Joop Eggen
  • 107,315
  • 7
  • 83
  • 138
1

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")
Gerard Reches
  • 3,048
  • 3
  • 28
  • 39
1

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.

Community
  • 1
  • 1
Ian2thedv
  • 2,691
  • 2
  • 26
  • 47
1

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".

Patricia Shanahan
  • 25,849
  • 4
  • 38
  • 75