views:

837

answers:

12

Please forgive the length, but here are two programs, both the exact same, but one with and one without setters, getters, and constructors.

I've taken a basic C++ class before and don't remember any of these from it, and at the moment I'm not seeing the point of them, if anyone could explain them in lamen's terms I'd much appreciate it...at the moment they seem to be nothing more than space wasters to make my code look longer, but the teacher says they are important (and so far that's it).

Thanks in advance! And now here's the code: Mileage.java:

package gasMileage;

import java.util.Scanner; //program uses class Scanner

public class Mileage 
{
    public int restart;
    public double miles, gallons, totalMiles, totalGallons, milesPerGallon;
    public Mileage(int newRestart, double newMiles, double newGallons, 
             double newTotalMiles, double newTotalGallons, double newMilesPerGallon)
    {
     setRestart(newRestart);
     setMiles(newMiles);
     setGallons(newGallons);
     setTotalMiles(newTotalMiles);
     setTotalGallons(newTotalGallons);
     setMilesPerGallon(newMilesPerGallon);
    }
    public void setRestart(int newRestart)
    {
     restart = newRestart;
    }
    public int getRestart()
    {
     return restart;
    }
    public void setMiles(double newMiles)
    {
     miles = newMiles;
    }
    public double getMiles()
    {
     return miles;
    }
    public void setGallons(double newGallons)
    {
     gallons = newGallons;
    }
    public double getGallons()
    {
     return gallons;
    }
    public void setTotalMiles(double newTotalMiles)
    {
     totalMiles = newTotalMiles;
    }
    public double getTotalMiles()
    {
     return totalMiles;
    }
    public void setTotalGallons(double newTotalGallons)
    {
     totalGallons = newTotalGallons;
    }
    public double getTotalGallons()
    {
     return totalGallons;
    }
    public void setMilesPerGallon(double newMilesPerGallon)
    {
     milesPerGallon = newMilesPerGallon;
    }
    public double getMilesPerGallon()
    {
     return milesPerGallon;
    }
    public void calculateMileage()
    {
     Scanner input = new Scanner(System.in);
     while(restart == 1)
     {
      System.out.print("Please input number of miles you drove: ");
      miles = input.nextDouble();
      totalMiles = totalMiles + miles;
      System.out.print("Please input number of gallons you used: ");
      gallons = input.nextDouble();
      totalGallons = totalGallons + gallons;
      milesPerGallon = miles / gallons;
      System.out.printf("Your mileage is %.2f MPG.\n", milesPerGallon);
      System.out.print("Would you like to try again? 1 for yes, 2 for no: ");
      restart = input.nextInt();
     }
     milesPerGallon = totalMiles / totalGallons;
     System.out.printf("Your total mileage for these trips is: %.2f.\nYour total gas consumed on these trips was: %.2f.\n", totalMiles, totalGallons);
     System.out.printf("Your total mileage for these trips is: %.2f MPG", milesPerGallon);
    }
}

Mileagetest.java:

package gasMileage;

public class Mileagetest 
{
    public static void main(String[] args) 
    {
     Mileage myMileage = new Mileage(1,0,0,0,0,0);
     myMileage.calculateMileage();
    }
}

And now for the one without setters and getters:

Testmileage.java:

package gasMileage;

import java.util.Scanner;

public class Testmileage 
{
    int restart = 1;
    double miles = 0, milesTotal = 0, gas = 0, gasTotal = 0, mpg = 0;
    Scanner input = new Scanner(System.in);
    public void testCalculate()
    {
     while(restart == 1)
     {
      System.out.print("Please input miles: ");
      miles = input.nextDouble();
      milesTotal = milesTotal + miles;
      System.out.print("Please input gas: ");
      gas = input.nextDouble();
      gasTotal = gasTotal + gas;
      mpg = miles/gas;
      System.out.printf("MPG: %.2f", mpg);
      System.out.print("\nContinue? 1 = yes, 2 = no: ");
      restart = input.nextInt();
     }
      mpg = milesTotal / gasTotal;
      System.out.printf("Total Miles: %.2f\nTotal Gallons: %.2f\nTotal MPG: %.2f\n", milesTotal, gasTotal, mpg);
    }
}

Testmileagetest.java:

package gasMileage;

public class Testmileagetest 
{

    /**
     * @param args
     */
    public static void main(String[] args) 
    {
     Testmileage test = new Testmileage();
     test.testCalculate();
    }

}

Thanks again!

+1  A: 

The point of accessor methods ie. getters and setters is to provide encapsulation AKA information hiding. It's one of the basic principles of object oriented programming.

Accessor methods

Information hiding/encapsulation

Asaph
+3  A: 

The point is that a class should not allow direct access to its fields, because this is implementation-specific. You may want to change the class later in order to use another data storage, but keep the class the same for its "users", or you may want to create an interface which cannot include fields either.

Have a look at the Wikipedia article on the subject.

Lucero
+16  A: 

The point of getters and setters, regardless of language, is to hide the underlying variable. This allows you to add verification logic when attempting to set a value - for example, if you had a field for a birth date, you might only want to allow setting that field to some time in the past. This cannot be enforced if the field is publicly accessible and modifyable - you need the getters and setters.

Even if you don't need any verification yet, you might need it in the future. Writing the getters and setters now means the interface is kept consistent, so existing code won't break when you change it.

Michael Madsen
I'm not exactly how to word this, but while I think I understand what you said, I don't grasp it? If that makes any sense?You're saying the sets/gets/constructors keep the variable hidden from the program, I understand that, but not the point of it? How is the code with setters/getters that much better/important than the code I wrote without? Both accomplish the exact same thing, but one does it in quite a bit less code. Not looking for a "less lines of code" way of coding...just trying to see the difference if that makes any sense?
Jeff
The variable is just a data storage, while getters and setters are abstracted ways of accessing or modifying data. Since the getters and setters are abstracted ways, later modifications to your data structure (like, adding verification, calculating some values, using a database, etc.) do not require your abstraction of getter and setter to be change, thus keeping all code which uses the class identical whilst changing the internal workings of the code.
Lucero
So...setters and getters are similar to making a copy of the variable, allowing tinkering with that copy so as to not mess with the original values?
Jeff
No. They are not a copy. It's like being in the restaurant: the waiter brings the food and returns the empty dishes, you don't have to know (or care) who in the kitchen is doing what to get your food. So if the restaurant makes fresh pasta or warms them in the microwave does not matter to you; the waiter abstracts the access to the food (and the tools) in the kitchen for you.
Lucero
So the waiter is the sets and gets and the food are the variable's values? And the kitchen is the actual variables themselves?
Jeff
I 90% agree with your answer, except the 'regardless of language' part. In some programming languages (I have python in mind, but there is others) you can define 'properties', that is, functions that have the same syntaxic form that variables. Hence you can use real internal attributes (publicly accessible fields) for some time, then change them for a property later when you need more precise control. It doesn't change a thing for external code. When such constructions are builtin in a language, I believe gettter and setters become less necessary.
kriss
A simple example: You have a "road" class that describes a stretch of theoretical road. you have a set function bool setLength(int x); Which allows you to set the length of that road. You don't want the user to set a negative distance, so inside the function you go: if (length <= 0) return false; In this way getters and setters restrict the manner in which users of your class can modify the class, disallowing improper use.
windfinder
Ack! It just clicked! I looked at mileagetest.java and stared at the line Mileage myMileage = new Mileage(1,0,0,0,0,0); for about 2 minutes before I realized "If I made a second one of those I could give it different values...but with my 'non-getter' code, I couldn't do that (I realize what I'm saying doesn't pertain to this program). Sorry it took so long for me to understand something that simple, but thank you for the assistance everyone!
Jeff
+4  A: 

The idea is that if your client classes call get/set functions, you can change what they do later and the callers are insulated. If you have a public variable, and I access it directly, there is no way for you to add behavior later when it is accessed or set.

Even in your simple example, you could take more advantage of it.

Instead of using:

milesPerGallon = miles / gallons;

in calculateMileage()

You could change setMiles() and setGallons() to update milesPerGallon when they were called. Then, remove setMilesPerGallon() to indicate that it's a read-only property.

Lou Franco
Could you explain a little further? What do you mean by "insulated"? And how could I change what they could do later? And why couldn't I add a behavior later if you access it directly?
Jeff
@Jeff: If you access miles, gallons, and milesPerGallon directly, milesPerGalllon wouldn't be updated, since there's no coding logic to do so.
R. Bemrose
by "insulated" I mean that that there are changes to the class you could make and other classes that used your class wouldn't need to change at all. For example, normally calculateMileage() wouldn't be in the Mileage class (it's the UI for an app, not the calculator) -- that other class (e.g. ConsoleMileageUI) would access Mileage by its public interface (get/set) -- the calculation of miles/gallon should stay in Mileage though -- perhaps be called in setMiles and setGallons -- or alternatively in getMilesPerGallon(). Which ever you chose, calculateMileage() wouldn't have to change.
Lou Franco
@R. Bemrose updates just fine in my second section of code, runs just as well as the first and I didn't use a single set or get in the second one@Lou Franco Sorry, but I'm afraid I don't understand what you mean still. I only named it calculateMileage because at the time I couldn't think of anything else to name it, I realize it contains both the UI and the calculating though.
Jeff
calculateMileage is a fine name, but the method doesn't belong in this class. Also, it should not be making the calculations -- the Mileage class should be doing the calculations -- calculateMileage should just be asking the user for values and calling setXXX with them -- then getXX to show answers -- everything about calculating the values should stay in the Mileage class. The point is not whether it works or not (you could do that a lot of different ways) but to make something that is easy to maintain and reuse.
Lou Franco
+2  A: 

They provide a public interface for your class, and some measure of encapsulation. Consider how you would access public data without getters and setters.

Mileage m = new Mileage();
m.miles = 5.0;
m.gallons = 10.0;
...

Now if you decide you want to add some validation to your class, you have to change your code everywhere that the fields were directly accessed. If you just use getters and setters from the start (only where they are needed) you can avoid that effort, and only change your code in one place.

Bill the Lizard
A: 

Fast forward a few months. Maybe your teacher asks you to implement a remote version of the Milage class. Maybe as a web service, maybe something else.

Without the getter/setters, you'd have to change every code everywhere that acccesses a Milage, with the getter/setters you pretty much(in a perfect world atleast) just have to change the creation of a Milage type.

leeeroy
+2  A: 

Using getters and setters gives you the flexibility to change the implementation later. You might not think you need that, but sometimes you do. For example, you might want to use the Proxy pattern to lazy load an object that is expensive to use:

class ExpensiveObject {
    private int foo;

    public ExpensiveObject() {
       // Does something that takes a long time.
    }

    public int getFoo() { return foo; }
    public void setFoo(int i) { foo = i; }
}

class ExpensiveObjectProxy extends ExpensiveObject {
    private ExpensiveObject realObject;

    public ExpensiveObjectProxy() { ; }

    protected void Load() {
       if ( realObject == null ) realObject = new ExpensiveObject();
    }

    public int getFoo() { Load(); return realObject.getFoo(); }
    public void setFoo(int i) { Load(); realObject.setFoo(i); }
}

class Main {
    public static void main( string[] args ) {
         // This takes no time, since ExpensiveOjbect is not constructed yet.
         ExpensiveObject myObj = new ExpensiveObjectProxy();

         // ExpensiveObject is actually constructed here, when you first use it.
         int i = myObj.getFoo();
    }
}

Where this often comes to play is when you have objects mapped to databases through an ORM. You only load the stuff you need, then go back to the database to load the rest if/when it is actually used.

Eric Petroelje
+1  A: 

The answer in one word is interfaces.

Interfaces allow for methods, not fields, so the established convention is to have getX and setX methods for this purpose.

(And interfaces is the way to decouple functionality from implementation in Java)

Thorbjørn Ravn Andersen
+3  A: 

Encapsulation

Accessor methods ("setters and getters") attempt to hide the details about how the data in an object is stored. In practice, they are a glorified means to store and retrieve data in a non-object-oriented fashion. Accessors do not effectively encapsulate anything in that there is little practical difference between the following two pieces of code:

Person bob = new Person();
Colour hair = bob.getHairColour();
hair.setRed( 255 );

And this:

Person bob = new Person();
Colour hair = bob.hairColour;
hair.red = 255;

Both code snippets expose the idea that a Person is tightly coupled to Hair. This tight coupling then reveals itself throughout the code base, resulting in brittle software. That is, it becomes difficult to change how a Person's hair is stored.

Instead:

Person bob = new Person();
bob.setHairColour( Colour.RED );

This follows the premise of "tell, don't ask." In other words, objects should be instructed (by other objects) to perform a specific task. This is the whole point of object-oriented programming. And very few people seem to get it.

The difference between the two scenarios is this:

  • In the first situation, Bob had no control over what colour his hair would become. Great for a hair stylist with a penchant for redheads, not so great for Bob who despises that colour.
  • In the second situation, Bob has complete control over what colour his hair will become because no other object in the system is allowed to change that colour without Bob's permission.

Another way to avoid this problem is to return a copy of Bob's hair colour (as a new instance), which is no longer coupled to Bob. I find that to be an inelegant solution because it means there is behaviour that another class desires, using a Person's hair, that is no longer associated with the Person itself. That reduces the ability to reuse code, which leads to duplicated code.

Hiding Data Types

In Java, which cannot have two method signatures that differ only by return type, it really does not hide the underlying data type used by the object. You will seldom, if ever, see the following:

public class Person {
  private long hColour = 1024;

  public Colour getHairColour() {
    return new Colour( hColour & 255, hColour << 8 & 255, hColour << 16 & 255 );
  }
}

Typically, the individual variables have their data type exposed verbatim by use of the corresponding accessor, and requires refactoring to change it:

public class Person {
  private long hColour= 1024;

  public long getHairColour() {
    return hColour;
  }

  /** Cannot exist in Java: compile error. */
  public Colour getHairColour() {
    return new Colour( hColour & 255, hColour << 8 & 255, hColour<< 16 & 255 );
  }
}

While it provides a level of abstraction, it is a thin veil that does nothing for loose coupling.

Tell, Don't Ask

For more information on this approach, read Tell, Don't Ask.

Dave Jarvis
A: 

Your example is extreme to the point of absurdity. Yes, all those getters and setters bloat the code and add no value in that case. But the underlying idea of encapsulation is meant for larger systems composed of many interacting components, not for small, self-contained programs.

Characteristics of useful, sensible uses of getters and setters:

  • A class that is used by many other classes (hiding implementation details makes it easier for the clients)
  • Getters and setters only for fields for which they're actually needed - as few as possible, most fields should be private and used only within their class
  • Very few setters in general: mutable fields make it much harder to keep track of the program's state than read-only fields
  • Getters and setters that actually do something besides accessing a fied, e.g. setters that throw exceptions for invalid values or update a "last modified" timestamp, or a getter that computes a value on the fly rather than relying on an underlying field
Michael Borgwardt
+8  A: 

The other answers generally give a good idea of some reasons for using getters and setters, but I want to give a somewhat complete example of why they are useful.

Let's take, for example, a file (ignoring the existence of a File class in Java). This File class has a field for storing the type of the file (.pdf, .exe, .txt, etc)... we'll ignore everything else.

Initially you decide to store it as a String with no getters and setters:

public class File {
   // ...
   public String type;
   // ...
}

Here are some issues with not using getters and setters.

No control over how the field is set:

Any clients of your class can do what they want with it:

public void doSomething(File file) {
   // ...
   file.type = "this definitely isn't a normal file type";
   // ...
}

You decide later that you probably do not want them to do that... but since they have direct access to the field in your class, you have no way of preventing it.

Inability to easily change internal representation:

Later still, you decide that you want to store the file type as an instance of an interface called FileType, allowing you to associate some behavior with different file types. However, many clients of your class are already retrieving and setting file types as Strings. So you'd have an issue there... you'd break a lot of code (even code in other projects that you can't fix yourself, if it's a library) if you just changed the field from a String to a FileType.

How Getters and Setters solve this

Now imagine that you had instead made the type field private and created

public String getType() {
   return this.type;
}

public void setType(String type) {
   this.type = type;
}

Control over setting the property:

Now, when you want to implement a requirement that only certain strings are valid file types and prevent other strings, you could just write:

public void setType(String type) {
   if(!isValidType(type)) {
       throw new IllegalArgumentException("Invalid file type: " + type);
   }
   this.type = type;
}

private boolean isValidType(String type) {
   // logic here
}

Ability to easily change internal representation:

Changing the String representation of the type is relatively easy. Imagine you have an enum ValidFileType which implements FileType and contains the valid types of files.

You could easily change the internal representation of the file type in the class like this:

public class File {
   // ...
   private FileType type;
   // ...
   public String getType() {
      return type.toString();
   }

   public void setType(String type) {
      FileType newType = ValidFileType.valueOf(type);

      if(newType == null) {
         throw new IllegalArgumentException("Invalid file type: " + type);
      }

      this.type = newType;
   }
}

Since clients of the class have been calling getType() and setType() anyway, nothing changes from their perspective. Only the internals of the class changed, not the interface that other classes are using.

ColinD
A: 

Getters and Setters Getters and Setters allow you to build useful shortcuts for accessing and mutating data within an object. Generally, this can be seen as an alternative to having two functions with an object that are used to get and set a value, like so: { getValue: function(){ return this._value; }, setValue: function(val){ this._value = val; } } The obvious advantage to writing JavaScript in this manner is that you can use it obscure values that you don't want the user to directly access. A final result looking something like the following (using a closure to store the value of a newly constructed Field): function Field(val){ var value = val;

this.getValue = function(){
    return value;
};

this.setValue = function(val){
    value = val;
};

}

Adding Setter and Getter Methods To make the state of the managed bean accessible, you need to add setter and getter methods for that state. The createSalutation method calls the bean’sgreet method, and the getSalutation method retrieves the result. Once the setter and getter methods have been added, the bean is complete. The final code looks like this: package greetings;

import javax.inject.Inject; import javax.enterprise.context.RequestScoped; import javax.inject.Named;

@Named @RequestScoped public class Printer {

@Inject @Informal Greeting greeting;

private String name;
private String salutation;

public void createSalutation() {
    this.salutation = greeting.greet(name);
}

public String getSalutation() {
    return salutation;
}
public String setName(String name) {
   this.name = name;
}

public String getName() {
   return name;
}

}

rodolfo