1.2. Implementation
LongInteger: implementation.
We are going to create a class called LongInteger
inheriting SignedNumeral
that can store an indefinite size of an integer value beyond the primitive types such as int
and long
.
What is so special about primitive data types in Java?
Java SE provides a similar class called BigInteger
although the implementations of LongInteger
andBigInteger
are completely independent.
Field
Let us declare the member field digits
that is an array of bytes holding the values of this integer:
L1
:LongInteger
is passed to specify the generic typeT
inSignedNumeral
.
The i
'th dimension of digits
is the i
'th least significant digit in the integer such that the integer 12345
would be stored as digits = {5, 4, 3, 2, 1}
, which makes it convenient to implement the arithmetic methods, add()
and multiply()
.
Is the array of bytes the most efficient way of storing a long integer?
Constructors
Let us define the following three constructors:
L2
: the default constructor that initializes this integer with0
by calling the constructor inL20
.L10
: a copy constructor that initializes this integer withn
.super()
: calls the corresponding constructor in the super class,SignedNumeral
.Arrays.copyOf()
: creates a new array by copyingn.digits
.
L20
: a constructor that initializes this integer withn
by passing it to theset()
method.
Do the constructors in
L10
andL20
call any constructor in the super class?
Arrays.copyOf()
is a static method referenced by the class type Arrays
, not an object. Java provides many classes with static methods that are commonly used (e.g., Arrays
, Collections
).
Can you call non-static methods or fields in the body of a static method?
The static
keyword must not be abused to quickly fix compile errors unless it is intended.
Method: set()
set()
Let us define the set()
method that takes a string and sets the sign and the value of this integer:
L1-7
: javadoc comments.L4
: this method throwsNullPointerException
ifn
is null.L5
: this method throwsInvalidParameterException
if the format ofn
is invalid.
L10-11
: throws theNullPointerException
.L14-18
: checks the first character ofn
and setsthis.sign
using the switch expression.String
member methods:charAt()
,substring()
.yield
: returns the value of this switch statement for the condition (introduced in Java 14).
L21-30
: sets the value ofn
tothis.digits
.L23
: for-loop can handle multiple variables such asi
andj
.L24
: gets the ASCII value ofn.charAt(i)
.L25-28
: throws theInvalidParameterException
ifv
is not a digit.L29
: stores the value in the reverse order.L27
:String.format()
is a static method inString
.
When should we use
throw
statements overtry..catch
blocks for error handling and vice versa?
This type of method is called a setter. Java encourages making member fields private and creating getters and setters to access the fields for encapsulation, which is not necessarily encouraged by other languages.
Method: add()
add()
Let us override the add()
method that calls two helper methods:
L3-4
: addsn
to this integer that has the same sign by callingaddSameSign()
.L5-6
: addsn
to this integer that has a different sign by callingaddDifferentSign()
.
The following shows an implementation of addSameSign()
based on the simple arithmetic:
L7-9
: creates the byte arrayresult
by copying values in this integer.L8
: the dimension ofresult
can be 1 more thanm
after the addition.Static methods:
Math.max()
,System.arraycopy()
.
L12-19
: addsn
toresults
(if exists) from the least significant digit.L15-18
: pass a carry to the next digit.
L22
: trims the most significant digit if it is0
.
What are tradeoffs to make the size of
result
to bem
instead ofm+1
and vice versa?
The following shows addDifferentSign()
that throws UnsupportedOperationException
:
The implementation of addDifferentSign()
is quite similar to addSameSign()
although it involves a few more logics. We will leave this as an exercise.
In practice, addSameSign()
and addDifferentSign()
should be private. We made them protected for exercise purposes.
Method: multiply()
multiply()
Let us override the multiply()
method:
L4
: sets the sign after the multiplication.L7-15
: multipliesn
to this integer:L7
: the max-dimension ofresults
isdigits.length + n.digits.length
.L12-13
: pass a carry to the next digit.
L18-20
: trims the most significant digit iteratively if it is0
.L20
:++m
incrementsm
before the comparison.
What is the worst-case complexity of the
multiply()
method?
Method: main()
main()
Let us create a runnable class called LongIntegerRun
that contains the main method:
Can we define the main method in
LongInteger
instead without creatingLongIntegerRun
?
L2
: the parameterargs
is passed from the command line.
Why does the main method need to be static?
This prints something like the following:
[
: one-dimensional array.L
: the element of this array is an object.java.lang.String
: the type of object.d716361
: the hash code of this array in hexadecimal.
What is the hash code of an object?
Every object implicitly inherits Object
that defines a few member methods including toString()
, which gets called automatically by the println()
method to retrieve the string representation of this object. We can use the helper method Arrays.toString()
that gives a more readable representation:
How is the
Arrays.toString()
method implemented?
Since no argument is passed to the main method at the moment, this prints an empty array:
If you set the arguments to 123 -456
using the [Run - Edit Configurations - Program arguments]
setting, it prints the following array:
Given those two arguments, we can create two integers:
This prints something like the following, which are returned by a.toString()
:
How is the
toString()
method implemented in theObject
class?
Method: toString()
toString()
To print a more readable representation, we need to override the toString()
method in LongInteger
:
L5
:StringBuilder
provides an efficient way of concatenating different data types into one string.
What are the advantages of using
StringBuilder
instead of concatenating values with the+
operator as follows:
Given the overridden method, the above main method now prints the following:
What are the advantages of overriding
toString()
instead of creating a new method with the same code, and calling the new method to get the string representation ofLongInteger
?
Method: compareTo()
compareTo()
Java does not allow operator overloading, so it is not possible to use logical operators to compare the two integers above, a
and b
:
In fact, any object that is comparable must inherit the interface Comparable
as follows:
L2
:LongInteger
is passed toComparable
as a generic type.
Is
extends
always used to inherit a class whereasimplements
is used to inherit an interface?
The Comparable
interface contains one abstract method called compareTo()
that returns a negative value if this object is smaller than n
, a positive value if this object is greater than n
, and zero if this object equals to n
. The compareTo()
method must be overridden by the LongInteger
class:
The compareAbs()
method compares the absolute values of this
and n
:
L7
: ifdigits
has more dimensions, its absolute value is greater.L10-13
: compares the significant digits iteratively.
Is it safe to use the same variable
i
to iterate bothdigits
andn.digits
?
Once LongInteger
properly inherits Comparable
by overriding compareTo()
, objects instantiated by this class can be compared using many built-in methods.
L2
:ArrayList
is a specific implementation of the interfaceList
.All collections in Java inheriting
AbstractCollection
uses generics.<>
: the diamond operator that infers the generic type from its declaration.
L11
: sorts the list in ascending order usingsort()
andComparator.naturalOrder()
.L14
: sorts the list in descending order usingsort()
andComparator.reverseOrder()
.
What is the advantage of declaring
list
asList
instead ofArrayList
? What kind of sorting algorithm doesCollections.sort()
use?
The above code prints the following sorted lists:
What would be the case that needs to distinguish
-0
from0
?
Last updated
Was this helpful?