forked from RunestoneInteractive/java4python
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathch_7_definingclasses.ptx
More file actions
937 lines (805 loc) · 37.4 KB
/
Copy pathch_7_definingclasses.ptx
File metadata and controls
937 lines (805 loc) · 37.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
<?xml version="1.0"?>
<!-- Generated by Docutils 0.19 -->
<chapter xml:id="classes-in-java">
<title>Classes in Java</title>
<section xml:id="defining-classes-in-java">
<title>Defining Classes in Java</title>
<introduction>
<p>
You have already seen how to define classes in Java.
It’s unavoidable for even the simplest of programs.
In this section we will look at how we define classes to create our own data types.
Lets start by creating a fraction class to extend the set of numeric data types provided by our language.
The requirements for this new data type are as follows:
</p>
<p>
<ul>
<li>
<p>
Given a numerator and a denominator create a new Fraction.
</p>
</li>
<li>
<p>
When a fraction is printed it should be simplified.
</p>
</li>
<li>
<p>
Two fractions can be added or subtracted
</p>
</li>
<li>
<p>
Two fractions can be multiplied or divided
</p>
</li>
<li>
<p>
Two fractions can be compared
</p>
</li>
<li>
<p>
A fraction and an integer can be added together.
</p>
</li>
<li>
<p>
Given a list of Fractions that list should be sortable by the default sorting function.
</p>
</li>
</ul>
</p>
<p>
Here is a mostly complete implementation of a Fraction class in Python that we will refer to throughout this section:
</p>
<program interactive="activecode" language="python">
<code>
class Fraction:
def __init__(self, num, den):
"""
:param num: The top of the fraction
:param den: The bottom of the fraction
"""
self.num = num
self.den = den
def __repr__(self):
if self.num > self.den:
retWhole = int(self.num / self.den)
retNum = self.num - (retWhole * self.den)
return str(retWhole) + " " + str(retNum) + "/" + str(self.den)
else:
return str(self.num) + "/" + str(self.den)
def show(self):
print(self.num, "/", self.den)
def __add__(self, other):
# convert to a fraction
other = self.toFract(other)
newnum = self.num * other.den + self.den * other.num
newden = self.den * other.den
common = gcd(newnum, newden)
return Fraction(int(newnum / common), int(newden / common))
__radd__ = __add__
def __lt__(self, other):
num1 = self.num * other.den
num2 = self.den * other.num
return num1 < num2
def toFract(self, n):
if isinstance(n, int):
other = Fraction(n, 1)
elif isinstance(n, float):
wholePart = int(n)
fracPart = n - wholePart
# convert to 100ths???
fracNum = int(fracPart * 100)
newNum = wholePart * 100 + fracNum
other = Fraction(newNum, 100)
elif isinstance(n, Fraction):
other = n
else:
print("Error: cannot add a fraction to a ", type(n))
return None
return other
def gcd(m, n):
"""
A helper function for Fraction
"""
while m % n != 0:
oldm = m
oldn = n
m = oldn
n = oldm % oldn
return n
print(sorted([Fraction(5, 16), Fraction(3, 16), Fraction(1, 16) + 1]))
</code> <tests> </tests>
</program>
<p>
The instance variables (data members) we will need for our fraction class are the numerator and denominator. Of course in Python we can add instance variables to a class at any time by simply assigning a value to <c>objectReference.variableName</c>, whereas in Java all data members must be declared up front.
</p>
<p>
The declarations of instance variables can come at the beginning of the class definition or the end. Cay Horstman, author of <url href="https://horstmann.com/corejava/index.html" visual="https://horstmann.com/corejava/index.html">the “Core Java” books</url> puts the declarations at the end of the class. I like them at the very beginning so you see the variables that are declared before you begin looking at the code that uses them. With that in mind the first part of the Fraction class definition is as follows:
</p>
<program language="java">
<code>
public class Fraction {
private Integer numerator;
private Integer denominator;
}
</code>
</program>
<p>
Notice that we have declared the numerator and denominator to be private.
This means that the compiler will generate an error if another method tries to write code like the following:
</p>
<program language="java">
<code>
Fraction f = new Fraction(1,2);
Integer y = f.numerator * 10;
</code>
</program>
<p>
Direct access to instance variables is not allowed.
Therefore if we legitimately want to be able to access information such as the numerator or denominator for a particular fraction we must have getter methods.
It is very common programming practice to provide getter and setter methods for instance variables in Java.
</p>
<program language="java">
<code>
public Integer getNumerator() {
return numerator;
}
public void setNumerator(Integer numerator) {
this.numerator = numerator;
}
public Integer getDenominator() {
return denominator;
}
public void setDenominator(Integer denominator) {
this.denominator = denominator;
}
</code>
</program>
</introduction>
</section>
<section xml:id="writing-a-constructor">
<title>Writing a constructor</title>
<p>
Once you have identified the instance variables for your class the next thing to consider is the constructor.
In Java, constructors have the same name as the class and are declared public.
They are declared without a return type.
So any method that is named the same as the class and has no return type is a constructor.
Our constructor will take two parameters: the numerator and the denominator.
</p>
<program language="java">
<code>
public Fraction(Integer top, Integer bottom) {
num = top;
den = bottom;
}
</code>
</program>
<p>
There are a couple of important things to notice here.
First, you will notice that the constructor does not have a <c>self</c> parameter.
You will also notice that we can simply refer to the instance variables by name without the <c>self</c> prefix, because they have already been declared.
This allows the Java compiler to do the work of dereferencing the current Java object.
Java does provide a special variable called <c>this</c> that works like the <c>self</c> variable.
In Java, <c>this</c> is typically only used when it is needed to differentiate between a parameter or local variable and an instance variable.
For example this alternate definition of the the Fraction constructor uses <c>this</c> to differentiate between parameters and instance variables.
</p>
<program language="java">
<code>
public Fraction(Integer num, Integer den) {
this.num = num;
this.den = den;
}
</code>
</program>
</section>
<section xml:id="methods">
<title>Methods</title>
<introduction>
<p>
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, <c>__add__</c> and <c>__lt__</c> change the behavior of <c>+</c> and <c><</c>, respectively.
In Java there is <term>no operator overloading</term>.
So we will have to write the method for addition a little differently.
</p>
<p>
A point of terminology: Python has both “functions” (<c>def</c> outside a class) and “methods” (<c>def</c> 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.”
</p>
<p>
Let’s begin by implementing addition in Java:
</p>
<program language="java">
<code>
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);
}
</code>
</program>
<p>
First you will notice that the <c>add</c> method is declared as <c>public Fraction</c> The <c>public</c> part means that any other method may call the <c>add</c> method.
The <c>Fraction</c> part means that <c>add</c> will return a fraction as its result.
</p>
<p>
Second, you will notice that the method makes use of the <c>this</c> variable.
In this method, <c>this</c> is not necessary, because there is no ambiguity about the <c>numerator</c> and <c>denominator</c> variables.
So this version of the code is equivalent:
</p>
<program language="java">
<code>
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);
}
</code>
</program>
<p>
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, <c>gcd</c>, is used to find a common divisor to simplify the numerator and denominator in the result.
</p>
<p>
Finally on line 6 a new <c>Fraction</c> 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.
</p>
</introduction>
<subsection>
<title>Method Signatures and Overloading</title>
<p>
Our specification for this project said that we need to be able to add a <c>Fraction</c> to an <c>Integer</c>.
In Python we can do this by checking the type of the parameter using the <c>isinstance</c> function at runtime.
Recall that <c>isinstance(1,int)</c> returns <c>True</c> to indicate that 1 is indeed an instance of the <c>int</c> class.
See the <c>__add__</c> and <c>toFract</c> methods in the Python version of the <c>Fraction</c> class to see how our Python implementation fulfills this requirement.
</p>
<p>
In Java we can do runtime type checking, but the compiler will not allow us to pass an Integer to the <c>add</c> method since the parameter has been declared to be a Fraction.
The way that we solve this problem is by writing another <c>add</c> method with a different set of parameters.
In Java this practice is legal and common we call this practice <term>method overloading</term>.
</p>
<p>
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.
</p>
<p>
To solve the problem of adding an <c>Integer</c> and a <c>Fraction</c> in Java we will overload both the constructor and the <c>add</c> method.
We will overload the constructor so that if it only receives a single <c>Integer</c> it will convert the <c>Integer</c> into a <c>Fraction</c>.
We will also overload the <c>add</c> method so that if it receives an <c>Integer</c> as a parameter it will first construct a <c>Fraction</c> from that integer and then add the two <c>Fractions</c> together.
The new methods that accomplish this task are as follows:
</p>
<program language="java">
<code>
public Fraction(Integer num) {
this.numerator = num;
this.denominator = 1;
}
public Fraction add(Integer other) {
return add(new Fraction(other));
}
</code>
</program>
<p>
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.
</p>
<p>
Our full <c>Fraction</c> class to this point would look like the following.
You should compile and run the program to see what happens.
</p>
<program interactive="activecode" language="java">
<code>
public class Fraction {
private Integer numerator;
private Integer denominator;
public Fraction(Integer num, Integer den) {
this.numerator = num;
this.denominator = den;
}
public Fraction(Integer num) {
this.numerator = num;
this.denominator = 1;
}
public Integer getNumerator() {
return numerator;
}
public Integer getDenominator() {
return denominator;
}
public Fraction add(Fraction other) {
Integer newNum = other.getDenominator()*this.numerator + this.denominator*other.getNumerator();
Integer newDen = this.denominator * other.getDenominator();
Integer common = gcd(newNum,newDen);
return new Fraction(newNum/common, newDen/common );
}
public Fraction add(Integer other) {
return add(new Fraction(other));
}
private static Integer gcd(Integer m, Integer n) {
while (m % n != 0) {
Integer oldm = m;
Integer oldn = n;
m = oldn;
n = oldm%oldn;
}
return n;
}
public static void main(String[] args) {
Fraction f1 = new Fraction(1,2);
System.out.println(f1.add(1));
}
}
</code> <tests> </tests>
</program>
</subsection>
</section>
<section xml:id="inheritance">
<title>Inheritance</title>
<introduction>
<p>
If you ran the program above you probably noticed that the output is not very satisfying. Chances are your output looked something like this:
</p>
<program language="java">
<code>
Fraction@6ff3c5b5
</code>
</program>
<p>
The reason is that we have not yet provided a friendly string representation for our <c>Fraction</c> objects.
Just like in Python, whenever an object is printed by the <c>println</c> method it must be converted to string format.
In Python you can control how that looks by writing an <c>__str__</c> method for your class.
If you do not then you will get the default, which looks something like the above.
</p>
</introduction>
<subsection>
<title>The <c>Object</c> Class</title>
<p>
In Java, the equivalent of <c>__str__</c> is the <c>toString</c> method.
Every object in Java already has a <c>toString</c> method defined for it because every class in Java automatically inherits from the <c>Object</c> class.
The <c>Object</c> class provides default implementations for the following methods.
</p>
<p>
<ul>
<li>
<p>
<c>clone</c>
</p>
</li>
<li>
<p>
<c>equals</c>
</p>
</li>
<li>
<p>
<c>finalize</c>
</p>
</li>
<li>
<p>
<c>getClass</c>
</p>
</li>
<li>
<p>
<c>hashCode</c>
</p>
</li>
<li>
<p>
<c>notify</c>
</p>
</li>
<li>
<p>
<c>notifyAll</c>
</p>
</li>
<li>
<p>
<c>toString</c>
</p>
</li>
<li>
<p>
<c>wait</c>
</p>
</li>
</ul>
</p>
<p>
We are not interested in most of the methods on that list, and many Java programmers live happy and productive lives without knowing much about most of the methods on that list.
However, to make our output nicer we will implement the <c>toString</c> method for the <c>Fraction</c> class.
A simple version of the method is provided below.
</p>
<program language="java">
<code>
public String toString() {
return numerator.toString() + "/" + denominator.toString();
}
</code>
</program>
<p>
The other important class for us to implement from the list of methods inherited from <c>Object</c> is the <c>equals</c> method.
In Java, when two objects are compared using the <c>==</c> operator they are tested to see if they are exactly the same object (that is, do the two objects occupy the same exact space in the computer’s memory?).
This is also the default behavior of the <c>equals</c> method provided by <c>Object</c>.
The <c>equals</c> method allows us to decide if two objects are equal by looking at their instance variables.
However it is important to remember that since Java does not have operator overloading <term>if you want to use your</term> <c>equals</c> <term>method you must call it directly</term>.
Therefore once you write your own <c>equals</c> method:
</p>
<program language="java">
<code>
object1 == object2
</code>
</program>
<p>
is NOT the same as
</p>
<program language="java">
<code>
object1.equals(object2)
</code>
</program>
<p>
Here is an <c>equals</c> method for the <c>Fraction</c> class:
</p>
<program language="java">
<code>
public boolean equals(Fraction other) {
Integer num1 = this.numerator * other.getDenominator();
Integer num2 = this.denominator * other.getNumerator();
if (num1 == num2)
return true;
else
return false;
}
</code>
</program>
<p>
One important thing to remember about <c>equals</c> is that it only checks to see if two objects are equal – it does not have any notion of less than or greater than.
We’ll see more about that shortly.
</p>
</subsection>
<subsection>
<title>Abstract Classes and Methods</title>
<p>
If we want to make our <c>Fraction</c> class behave like <c>Integer</c>, <c>Double</c>, and the other numeric classes in Java then we need to make a couple of additional modifications to the class.
The first thing we will do is plug <c>Fraction</c> into the Java class hierarchy at the same place as <c>Integer</c> and its siblings.
If you look at the documentation for <c>Integer</c> you will see that <c>Integer</c>’s parent class is <c>Number</c>.
Number is an <term>abstract class</term> that specifies several methods that all of its children must implement.
In Java an abstract class is more than just a placeholder for common methods.
In Java an abstract class has the power to specify certain methods that all of its children <term>must</term> implement.
You can trace this power back to the strong typing nature of Java.
</p>
<p>
Here is code that makes the <c>Fraction</c> class a child of <c>Number</c>:
</p>
<program language="java">
<code>
public class Fraction extends Number {
...
}
</code>
</program>
<p>
The keyword <c>extends</c> tells the compiler that the class <c>Fraction</c> extends, or adds new functionality to the <c>Number</c> class.
A child class always extends its parent.
</p>
<p>
The methods we must implement if <c>Fraction</c> is going to be a child of <c>Number</c> are:
</p>
<p>
<ul>
<li>
<p>
<c>longValue</c>
</p>
</li>
<li>
<p>
<c>intValue</c>
</p>
</li>
<li>
<p>
<c>floatValue</c>
</p>
</li>
<li>
<p>
<c>doubleValue</c>
</p>
</li>
</ul>
</p>
<p>
This really isn’t much work for us to implement these methods, as all we have to do is some type conversion and some division:
</p>
<program language="java">
<code>
public double doubleValue() {
return numerator.doubleValue() / denominator.doubleValue();
}
public float floatValue() {
return numerator.floatValue() / denominator.floatValue();
}
public int intValue() {
return numerator.intValue() / denominator.intValue();
}
public long longValue() {
return numerator.longValue() / denominator.longValue();
}
</code>
</program>
<p>
By having the <c>Fraction</c> class extend the <c>Number</c> class we can now pass a <c>Fraction</c> to any Java method that specifies it can receive a <c>Number</c> as one of its parameters.
For example many Java user interface methods accept any object that is a subclass of <c>Number</c> as a parameter.
In Java the class hierarchy and the “is-a” relationships are very important.
Whereas in Python you can pass any kind of object as a parameter to any method or function, the strong typing of Java makes sure that you only pass an object as a parameter that is of the type specified in the method signature, or one of the children of the type specified.
When you see a parameter of type <c>Number</c> it’s important to remember that an <c>Integer</c> <em>is-a</em> <c>Number</c> and a <c>Double</c> <em>is-a</em> <c>Number</c> and a <c>Fraction</c> <em>is-a</em> <c>Number</c>, because these classes are children of <c>Number</c>.
</p>
<p>
However, and this is a big however, it is important to remember that if you specify <c>Number</c> as the type of a particular parameter then the Java compiler will <term>only let you use the methods of a</term> <c>Number</c>: <c>longValue</c>, <c>intValue</c>, <c>floatValue</c>, and <c>doubleValue</c>.
</p>
<p>
Suppose you try to define a method as follows:
</p>
<program language="java">
<code>
public void test(Number a, Number b) {
a.add(b);
}
</code>
</program>
<p>
The Java compiler would give an error because <c>add</c> is not a defined method of the <c>Number</c> class.
You will <term>still get this error</term> even if all your code that calls this <c>test</c> method passes two <c>Fractions</c> as parameters (remember that <c>Fraction</c> does implement <c>add</c>).
</p>
</subsection>
</section>
<section xml:id="interfaces">
<title>Interfaces</title>
<p>
Lets turn our attention to making a list of fractions sortable by the standard Java sorting method <c>Collections.sort</c>.
In Python, we would just need to implement the <c>__cmp__</c> method.
But in Java we cannot be that informal.
In Java, things that are sortable must be <c>Comparable</c>.
Your first thought might be that <c>Comparable</c> is superclass of <c>Number</c>, but that is actually not the case.
Java only supports single inheritance, that is, a class can have only one parent.
Although it would be possible to add an additional layer to the class hierarchy it would also complicate things dramatically, because not only are <c>Numbers</c> comparable, but <c>Strings</c> are also <c>Comparable</c> as would many other types.
For example, we might have a <c>Student</c> class and we want to be able to sort students by their GPA.
But <c>Student</c> might already extends the class <c>Person</c> for which there would be no natural comparison method.
</p>
<p>
Java’s answer to this problem is the <c>Interface</c> mechanism.
Interfaces are like a combination of “inheritance” and “contracts” all rolled into one.
An interface is a <em>specification</em> that says any object that claims it implements this interface must provide the following methods.
It sounds a little bit like an abstract class, however it is outside the inheritance mechanism.
You can never create an instance of <c>Comparable</c>.
Many objects, however, do implement the <c>Comparable</c> interface.
What does the <c>Comparable</c> interface specify?
</p>
<p>
The <c>Comparable</c> interface says that any object that claims to be <c>Comparable</c> must implement the <c>compareTo</c> method.
Here is an excerpt from <url href="https://docs.oracle.com/javase/7/docs/api/java/lang/Comparable.html#compareTo(T)" visual="https://docs.oracle.com/javase/7/docs/api/java/lang/Comparable.html#compareTo(T)">the official documentation</url> for the <c>compareTo</c> method as specified by the <c>Comparable</c> interface.
</p>
<program language="text">
<code>
int compareTo(T o)
Compares this object with the specified object for order. Returns a
negative integer, zero, or a positive integer as this object is less
than, equal to, or greater than the specified object. The
implementor must ensure sgn(x.compareTo(y)) == -sgn(y.compareTo(x)) for
all x and y. (This implies that x.compareTo(y) must throw an exception
iff y.compareTo(x) throws an exception.)
...
</code>
</program>
<p>
To make our <c>Fraction</c> class <c>Comparable</c> we must modify the class declaration line as follows:
</p>
<program language="java">
<code>
public class Fraction extends Number implements Comparable<Fraction> {
...
}
</code>
</program>
<p>
The specification <c>Comparable<Fraction></c> makes it clear that <c>Fraction</c> is only comparable with another <c>Fraction</c>.
The <c>compareTo</c> method could be implemented as follows:
</p>
<program language="java">
<code>
public int compareTo(Fraction other) {
Integer num1 = this.numerator * other.getDenominator();
Integer num2 = this.denominator * other.getNumerator();
return num1 - num2;
}
</code>
</program>
</section>
<section xml:id="static-member-variables">
<title>Static member variables</title>
<p>
Suppose that you wanted to write a Student class so that the class could keep track of the number of students it had created.
Although you could do this with a global counter variable that is an ugly solution.
The right way to do it is to use a static variable.
In Python we could do this as follows:
</p>
<program interactive="activecode" language="python">
<code>
class Student:
numStudents = 0
def __init__(self, id, name):
self.id = id
self.name = name
Student.numStudents = Student.numStudents + 1
def main():
for i in range(10):
s = Student(i,"Student-"+str(i))
print('Number of students:', Student.numStudents)
main()
</code> <tests> </tests>
</program>
<p>
In Java we would write this same example using a static declaration.
</p>
<program interactive="activecode" language="java">
<code>
public class Student {
public static Integer numStudents = 0;
private int id;
private String name;
public Student(Integer id, String name) {
this.id = id;
this.name = name;
numStudents = numStudents + 1;
}
public static void main(String[] args) {
for(Integer i = 0; i < 10; i++) {
Student s = new Student(i,"Student"+i.toString());
}
System.out.println("Number of students: "+Student.numStudents.toString());
}
}
</code> <tests> </tests>
</program>
<p>
In this example notice that we create a static member variable by using the <c>static</c> modifier on the variable declaration. Once a variable has been declared <c>static</c> in Java it can be accessed from inside the class without prefixing the name of the class as we had to do in Python.
</p>
</section>
<section xml:id="static-methods">
<title>Static Methods</title>
<p>
We have already discussed the most common static method of all, <c>main</c>. However in our <c>Fraction</c> class we also implemented a method to calculate the greatest common divisor for two fractions (<c>gdc</c>). There is no reason for this method to be a member method since it takes two <c>Integer</c> values as its parameters. Therefore we declare the method to be a static method of the class. Furthermore, since we are only going to use this <c>gcd</c> method for our own purposes we can make it <c>private</c>.
</p>
<program language="java">
<code>
private static Integer gcd(Integer m, Integer n) {
while (m % n != 0) {
Integer oldm = m;
Integer oldn = n;
m = oldn;
n = oldm%oldn;
}
return n;
}
</code>
</program>
</section>
<section xml:id="full-implementation-of-the-fraction-class">
<title>Full Implementation of the Fraction Class</title>
<p>
Here is a final version of the <c>Fraction</c> class in Java, which includes all the features we discussed:
</p>
<program interactive="activecode" language="java">
<code>
import java.util.ArrayList;
import java.util.Collections;
public class Fraction extends Number implements Comparable<Fraction> {
private Integer numerator;
private Integer denominator;
/** Creates a new instance of Fraction */
public Fraction(Integer num, Integer den) {
this.numerator = num;
this.denominator = den;
}
public Fraction(Integer num) {
this.numerator = num;
this.denominator = 1;
}
public Fraction add(Fraction other) {
Integer newNum = other.getDenominator()*this.numerator + this.denominator*other.getNumerator();
Integer newDen = this.denominator * other.getDenominator();
Integer common = gcd(newNum,newDen);
return new Fraction(newNum/common, newDen/common);
}
public Fraction add(Integer other) {
return add(new Fraction(other));
}
public Integer getNumerator() {
return numerator;
}
public void setNumerator(Integer numerator) {
this.numerator = numerator;
}
public Integer getDenominator() {
return denominator;
}
public void setDenominator(Integer denominator) {
this.denominator = denominator;
}
public String toString() {
return numerator.toString() + "/" + denominator.toString();
}
public boolean equals(Fraction other) {
Integer num1 = this.numerator * other.getDenominator();
Integer num2 = this.denominator * other.getNumerator();
if (num1 == num2)
return true;
else
return false;
}
public double doubleValue() {
return numerator.doubleValue() / denominator.doubleValue();
}
public float floatValue() {
return numerator.floatValue() / denominator.floatValue();
}
public int intValue() {
return numerator.intValue() / denominator.intValue();
}
public long longValue() {
return numerator.longValue() / denominator.longValue();
}
public int compareTo(Fraction other) {
Integer num1 = this.numerator * other.getDenominator();
Integer num2 = this.denominator * other.getNumerator();
return num1 - num2;
}
private static Integer gcd(Integer m, Integer n) {
while (m % n != 0) {
Integer oldm = m;
Integer oldn = n;
m = oldn;
n = oldm%oldn;
}
return n;
}
public static void main(String[] args) {
Fraction f1 = new Fraction(1,2);
Fraction f2 = new Fraction(2,3);
Fraction f3 = new Fraction(1,4);
System.out.println("Adding: " + f1.add(1));
System.out.println("Calling intValue(): " + f1.intValue());
System.out.println("Calling doubleValue(): " + f1.doubleValue());
ArrayList<Fraction> myFracs = new ArrayList<Fraction>();
myFracs.add(f1);
myFracs.add(f2);
myFracs.add(f3);
Collections.sort(myFracs);
System.out.println("Sorted fractions:");
for(Fraction f : myFracs) {
System.out.println(f);
}
}
}
</code> <tests> </tests>
</program>
</section>
</chapter>