Skip to main content

Java For Python Programmers Edition 2

Section 6.3 Methods

Now we come to one of the major differences between Java and Python. The Python class definition used the special methods for addition and comparison that have the effect of redefining how the standard operators behave: in Python, __add__ and __lt__ change the behavior of + and <, respectively. In Java there is no operator overloading. So we will have to write the method for addition a little differently.
A point of terminology: Python has both β€œfunctions” (def outside a class) and β€œmethods” (def inside a class). Since Java requires all code to be inside classes, it only has β€œmethods.” Those from a C++ background might refer to methods as β€œmember functions.”
Before we dive into the add method, it’s important to understand how Java passes arguments to methods, as this is a common point of confusion for programmers coming from Python. The terminology is different, but the practical result for objects is effectively identical.
  • Java is strictly pass-by-value. For primitive types (like int), a copy of the value is passed. For object types (like our Fraction), a copy of the value of the reference (the memory address) is passed.
  • Python is pass-by-assignment (or pass-by-object-reference). Since everything in Python is an object, the rule is consistent: a copy of the reference to the object is passed.
What does this mean in practice? For objects, the behavior is the same in both languages. When you pass a Fraction object to the add method, both the original variable outside the method and the parameter inside the method (otherFrac) refer to the exact same object in memory. This allows the method to use the object’s getters to read its state. If you were to call a setter method on otherFrac, the change would be reflected in the original object.
However, if you reassign the parameter to a completely new object inside the method (e.g., otherFrac = new Fraction(0,1);), it would not affect the original variable outside the method, because you are only changing the local copy of the reference.
Let’s begin by implementing addition in Java:
public Fraction add(Fraction otherFrac) {
    Integer newNum = otherFrac.getDenominator() * this.numerator +
                             this.denominator * otherFrac.getNumerator();
    Integer newDen = this.denominator * otherFrac.getDenominator();
    Integer common = gcd(newNum, newDen);
    return new Fraction(newNum/common, newDen/common);
}
First you will notice that the add method is declared as public Fraction The public part means that any other method may call the add method. The Fraction part means that add will return a fraction as its result.
Second, you will notice that the method makes use of the this variable. In this method, this is not necessary, because there is no ambiguity about the numerator and denominator variables. So the following version of the code is equivalent:
public Fraction add(Fraction otherFrac) {
    Integer newNum = otherFrac.getDenominator() * numerator +
                             denominator * otherFrac.getNumerator();
    Integer newDen = denominator * otherFrac.getDenominator();
    Integer common = gcd(newNum, newDen);
    return new Fraction(newNum/common, newDen/common);
}
The addition takes place by multiplying each numerator by the opposite denominator before adding. This procedure ensures that we are adding two fractions with common denominators. Using this approach the denominator is computed by multiplying the two denominators. The greatest common divisor method, gcd, is used to find a common divisor to simplify the numerator and denominator in the result.
Finally on line 6 a new Fraction is returned as the result of the computation. The value that is returned by the return statement must match the value that is specified as part of the declaration. So, in this case the return value on line 8 must match the declared value on line 1.

Subsection 6.3.1 Method Signatures and Overloading

Our specification for this project said that we need to be able to add a Fraction to an Integer. In Python we can do this by checking the type of the parameter using the isinstance function at runtime. Recall that isinstance(1,int) returns True to indicate that 1 is indeed an instance of the int class. See the __add__ and toFract methods in the Python version of the Fraction class to see how our Python implementation fulfills this requirement.
In Java we can do runtime type checking, but the compiler will not allow us to pass an Integer to the add method since the parameter has been declared to be a Fraction. The way that we solve this problem is by writing another add method with a different set of parameters. In Java this practice is legal and common we call this practice method overloading.
This idea of method overloading raises a very important difference between Python and Java. In Python a method is known by its name only. In Java a method is known by its signature. The signature of a method includes its name, and the types of all of its parameters. The name and the types of the parameters are enough information for the Java compiler to decide which method to call at runtime.
To solve the problem of adding an Integer and a Fraction in Java we will overload both the constructor and the add method. We will overload the constructor so that if it only receives a single Integer it will convert the Integer into a Fraction. We will also overload the add method so that if it receives an Integer as a parameter it will first construct a Fraction from that integer and then add the two Fractions together. The new methods that accomplish this task are as follows:
public Fraction(Integer num) {
    this.numerator = num;
    this.denominator = 1;
}
public Fraction add(Integer other) {
    return add(new Fraction(other));
}
Notice that the overloading approach can provide us with a certain elegance to our code. Rather than utilizing if statements to check the types of parameters we just overload methods ahead of time which allows us to call the method we want and allow the compiler to make the decisions for us. This way of thinking about programming takes some practice.
Our full Fraction class to this point would look like the following. You should compile and run the program to see what happens.
You have attempted of activities on this page.