arrow-left

All pages
gitbookPowered by GitBook
1 of 5

Loading...

Loading...

Loading...

Loading...

Loading...

1.2. Implementation

LongInteger: implementation.

We are going to create a class called LongIntegerarrow-up-right inheriting SignedNumeral that can store an indefinite size of an integer value beyond the primitive typesarrow-up-right such as int and long.

What is so special about primitive data types in Java?

circle-info

Java SE provides a similar class called although the implementations of LongIntegerandBigIntegerare completely independent.

hashtag
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 type T in SignedNumeral.

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?

hashtag
Constructors

Let us define the following three constructors:

  • L2: the default constructor that initializes this integer with 0 by calling the constructor in L20.

  • L10: a copy constructor that initializes this integer with n.

Do the constructors in L10 and L20 call any constructor in the super class?

Arrays.copyOf() is a referenced by the class type Arrays, not an object. Java provides many classes with static methods that are commonly used (e.g., , ).

Can you call non-static methods or fields in the body of a static method?

circle-info

The static keyword must not be abused to quickly fix compile errors unless it is intended.

hashtag
Method: 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 throws if n is null.

When should we use statements over blocks for error handling and vice versa?

circle-info

This type of method is called a setter. Java encourages making member fields private and creating getters and setters to access the fields for , which is not necessarily encouraged by other languages.

hashtag
Method: add()

Let us override the add() method that calls two helper methods:

  • L3-4: adds n to this integer that has the same sign by calling addSameSign().

  • L5-6: adds n to this integer that has a different sign by calling addDifferentSign().

The following shows an implementation of addSameSign() based on the simple arithmetic:

  • L7-9: creates the byte array result by copying values in this integer.

    • L8: the dimension of result can be 1 more than m after the addition.

What are tradeoffs to make the size of result to be m instead of m+1 and vice versa?

The following shows addDifferentSign() that throws :

The implementation of addDifferentSign() is quite similar to addSameSign() although it involves a few more logics. We will leave this as an .

circle-info

In practice, addSameSign()and addDifferentSign() should be private. We made them protected for exercise purposes.

hashtag
Method: multiply()

Let us override the multiply() method:

  • L4: sets the sign after the multiplication.

  • L7-15: multiplies n to this integer:

What is the worst-case complexity of the multiply() method?

hashtag
Method: main()

Let us create a runnable class called that contains the main method:

Can we define the main method in LongInteger instead without creating LongIntegerRun?

  • L2: the parameter args 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

What is the hash code of an object?

Every object implicitly inherits that defines a few member methods including , which gets called automatically by the println() method to retrieve the string representation of this object. We can use the helper method 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 the Object class?

hashtag
Method: toString()

To print a more readable representation, we need to override the toString() method in LongInteger:

  • L5: 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 of LongInteger?

hashtag
Method: compareTo()

Java does not allow , 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 as follows:

  • L2: LongInteger is passed to Comparable as a generic type.

Is extends always used to inherit a class whereas implements is used to inherit an interface?

The Comparable interface contains one abstract method called 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: if digits 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 both digits and n.digits?

Once LongInteger properly inherits Comparable by overriding compareTo(), objects instantiated by this class can be compared using many built-in methods.

  • L2: is a specific implementation of the interface .

    • All collections in Java inheriting uses generics.

What is the advantage of declaring list as List instead of ArrayList? What kind of sorting algorithm does Collections.sort() use?

The above code prints the following sorted lists:

What would be the case that needs to distinguish -0 from 0?

  • super(): calls the corresponding constructor in the super class, SignedNumeral.

  • Arrays.copyOf()arrow-up-right: creates a new array by copying n.digits.

  • L20: a constructor that initializes this integer with n by passing it to the set() method.

  • L5: this method throws InvalidParameterExceptionarrow-up-right if the format of n is invalid.
  • L10-11: throws the NullPointerException.

  • L14-18: checks the first character of n and sets this.sign using the switcharrow-up-right expression.

    • String member methods: charAt()arrow-up-right, substring()arrow-up-right.

    • yield: returns the value of this switch statement for the condition (introduced in Java 14).

  • L21-30: sets the value of n to this.digits .

    • L23: for-loop can handle multiple variables such as i and j.

    • L24: gets the value of n.charAt(i).

    • L25-28: throws the InvalidParameterException if v is not a digit.

    • L29: stores the value in the reverse order.

    • L27: is a static method in String.

  • Static methods: Math.max()arrow-up-right, System.arraycopy()arrow-up-right.

  • L12-19: adds n to results (if exists) from the least significant digit.

    • L15-18: pass a carry to the next digit.

  • L22: trims the most significant digit if it is 0.

  • L7: the max-dimension of results is digits.length + n.digits.length.
  • L12-13: pass a carry to the next digit.

  • L18-20: trims the most significant digit iteratively if it is 0.

    • L20: ++m increments m before the comparison.

  • : the type of object.
  • d716361: the hash code of this array in hexadecimal.

  • <>
    : the
    diamond
    operator that infers the generic type from its declaration.
  • L11: sorts the list in ascending order using sort()arrow-up-right and Comparator.naturalOrder()arrow-up-right.

  • L14: sorts the list in descending order using sort()arrow-up-right and Comparator.reverseOrder()arrow-up-right.

  • BigIntegerarrow-up-right
    static methodarrow-up-right
    Arraysarrow-up-right
    Collectionsarrow-up-right
    NullPointerExceptionarrow-up-right
    throwarrow-up-right
    try..catcharrow-up-right
    encapsulationarrow-up-right
    UnsupportedOperationExceptionarrow-up-right
    exercise
    LongIntegerRunarrow-up-right
    Objectarrow-up-right
    toString()arrow-up-right
    Arrays.toString()arrow-up-right
    StringBuilderarrow-up-right
    operator overloadingarrow-up-right
    Comparablearrow-up-right
    compareTo()arrow-up-right
    ArrayListarrow-up-right
    Listarrow-up-right
    AbstractCollectionarrow-up-right

    1.4. Quiz

    Quiz 1: Java Essentials

    hashtag
    Coding

    • Create a class called under the main package that extends the class.

    1.1. Abstraction

    Different types of objects and inheritances in Java.

    hashtag
    Class

    A is a template to instantiate an .

    What is the relationship between a class and an object?

    Let us create a class called

    1. Java Essentials

    This chapter explains essential object-oriented programming features in Java to implement data structures and algorithms.

    hashtag
    Contents

    public class LongInteger extends SignedNumeral<LongInteger> {
        /** The values of this integer (excluding the sign). */
        protected byte[] digits;
        ...
    /** Creates a long integer with the default value of "0". */
    public LongInteger() {
        this("0");
    }
    
    /**
     * Creates a long integer by copying the specific object.
     * @param n the object to be copied.
     */
    public LongInteger(LongInteger n) {
        super(n.sign);
        digits = Arrays.copyOf(n.digits, n.digits.length);
    }
    
    /**
     * Creates a long integer with the specific sign and values.
     * @param n the sign and values to be set.
     * @see #set(String)
     */
    public LongInteger(String n) {
        set(n);
    }
    /**
     * Sets the sign and values of this integer.
     * @param n the sign and values to be set.
     * @throws NullPointerException when `n` is null.
     * @throws InvalidParameterException when `n` contains non-digit character
     *         except for the first character that can be [+-\d].
     */
    public void set(String n) {
        // 'n' must not be null
        if (n == null)
            throw new NullPointerException();
    
        // set this.sign
        sign = switch (n.charAt(0)) {
            case '-' -> { n = n.substring(1); yield Sign.NEGATIVE; }
            case '+' -> { n = n.substring(1); yield Sign.POSITIVE; }
            default -> Sign.POSITIVE;
        };
    
        // set this.digits
        digits = new byte[n.length()];
    
        for (int i = 0, j = n.length() - 1; i < n.length(); i++, j--) {
            byte v = (byte)(n.charAt(i) - 48);
            if (0 > v || v > 9) {
                String s = String.format("%d is not a valid value", v);
                throw new InvalidParameterException(s);
            }
            digits[j] = v;
        }
    }
    @Override
    public void add(LongInteger n) {
        if (sign == n.sign)
            addSameSign(n);
        else
            addDifferentSign(n);
    }
    /**
     * Adds the specific integer that has the same sign as this integer.
     * @param n the integer to be added with the same sign.
     */
    protected void addSameSign(LongInteger n) {
        // copy this integer to result[]
        int m = Math.max(digits.length, n.digits.length);
        byte[] result = new byte[m + 1];
        System.arraycopy(digits, 0, result, 0, digits.length);
    
        // add n to result
        for (int i = 0; i < n.digits.length; i++) {
            if (i < n.digits.length)
                result[i] += n.digits[i];
            if (result[i] >= 10) {
                result[i] -= 10;
                result[i + 1] += 1;
            }
        }
    
        // set this.digits
        digits = result[m] == 0 ? Arrays.copyOf(result, m) : result;
    }
    /**
     * Adds the specific integer that has a different sign from this integer.
     * @param n the integer to be added with a different sign
     */
    protected void addDifferentSign(LongInteger n) {
        throw new UnsupportedOperationException();
    }
    @Override
    public void multiply(LongInteger n) {
        // set this.sign
        sign = (sign == n.sign) ? Sign.POSITIVE : Sign.NEGATIVE;
    
        // multiply this and n and save it to result
        byte[] result = new byte[digits.length + n.digits.length];
        for (int i = 0; i < digits.length; i++) {
            for (int j = 0; j < n.digits.length; j++) {
                int k = i + j, prod = digits[i] * n.digits[j];
                result[k] += prod;
                result[k + 1] += result[k] / 10;
                result[k] %= 10;
            }
        }
    
        // set this.digits
        int m; for (m = result.length - 1; m > 0; m--)
            if (result[m] != 0) break;
        digits = ++m < result.length ? Arrays.copyOf(result, m) : result;
    }
    public class LongIntegerRun {
        static public void main(String[] args) {
            System.out.println(args);
        }
    }
    [Ljava.lang.String;@d716361
    static public void main(String[] args) {
        System.out.println(Arrays.toString(args));
    }
    []
    [123, -456]
    static public void main(String[] args) {    
        LongInteger a = new LongInteger(args[0]);
        LongInteger b = new LongInteger(args[1]);
        
        System.out.println(a);
        System.out.println(b);
    }
    edu.emory.cs.algebraic.LongInteger@4d7e1886
    edu.emory.cs.algebraic.LongInteger@3cd1a2f1
    public class LongInteger extends SignedNumeral<LongInteger> {
        ...
        @Override
        public String toString() {
            StringBuilder build = new StringBuilder();
            if (sign == Sign.NEGATIVE) build.append("-");
            for (int i = digits.length - 1; i >= 0; i--)
                build.append(digits[i]);
            return build.toString();
        }
        ...
    String s = "";
    if (sign == Sign.NEGATIVE) s += "-";
    for (int i = digits.length - 1; i >= 0; i--)
        s += digits[i];
    return s;
    123
    -456
    boolean c = a < b;  // gives a compile error
    public class LongInteger extends SignedNumeral<LongInteger> 
                             implements Comparable<LongInteger> {
    ...
    }
    @Override
    public int compareTo(LongInteger n) {
        if (isPositive())
            return n.isNegative() ? 1 : compareAbs(n);
        else
            return n.isPositive() ? -1 : -compareAbs(n);
    }
    /**
     * @param n the object to be compared.
     * @return a negative integer, zero, or a positive integer as the absolute value of this object is
     * less than, equal to, or greater than the absolute value of the specified object.
     */
    public int compareAbs(LongInteger n) {
        int diff = digits.length - n.digits.length;
    
        if (diff == 0) {
            for (int i = digits.length - 1; i >= 0; i--) {
                diff = digits[i] - n.digits[i];
                if (diff != 0) break;
            }
        }
    
        return diff;
    }
    static public void main(String[] args) {
        List<LongInteger> list = new ArrayList<>();
        
        list.add(new LongInteger("78"));
        list.add(new LongInteger("-45"));
        list.add(new LongInteger("0"));
        list.add(new LongInteger("6"));
        list.add(new LongInteger("-0"));
        list.add(new LongInteger("-123"));
        
        list.sort(Comparator.naturalOrder());
        System.out.println(list);
    
        list.sort(Comparator.reverseOrder());
        System.out.println(list);
    }
    [-123, -45, -0, 0, 6, 78]
    [78, 6, 0, -0, -45, -123]
    ASCIIarrow-up-right
    String.format()arrow-up-right

    Override the addDifferentSign()arrow-up-right method so that the add() method in LongIntegerQuizarrow-up-right can handle integers with different signs.

    hashtag
    Testing

    • Create the LongIntegerQuizTestarrow-up-right class under the test algebraicarrow-up-right package.

    • Test the correctness of your LongIntegerQuiz using the unit tests.

    • Add more tests for a more thorough assessment if necessary.

    hashtag
    Quizzes

    1. What is the advantage of using Generics?

    2. How do you make the class you define comparable?

    3. What is the advantage of overriding member methods in the Object class?

    4. What kind of methods should be defined as static?

    hashtag
    Submission

    1. Commit and push everything under the following packages to your GitHub repository:

    • Main: src/main/java/edu/emory/cs/algebraicarrow-up-right

    • Test: src/test/java/edu/emory/cs/algebraicarrow-up-right

    2. Submit answers to the above quizzes to Canvas.

    LongIntegerQuizarrow-up-right
    algebraicarrow-up-right
    LongIntegerarrow-up-right
    Implementation
  • Unit Testing

  • Quiz

  • hashtag
    Resources

    • Main: src/main/java/edu/emory/cs/algebraicarrow-up-right

    • Test: src/test/java/edu/emory/cs/algebraicarrow-up-right

    hashtag
    References

    • TIOBE Programming Community Indexarrow-up-right

    • PYPL Popularity of Programming Languagearrow-up-right

    circle-info

    Please follow every example described in this section. Programming is an act of writing, not reading. By the end of this chapter, you should be able to reproduce the entire codebase yourself from scratch without consulting those examples.

    Abstraction
    that we want to be a super class of all numeral types:
    • L1: packageindicates the name of the package that this class belongs to in a hierarchy.

    • L3: public is an access-level modifierarrow-up-right.

    What are the acceptable access-level modifiers to declare a top-level class?

    Let us declare a method, add() , that is an operation expected by all numeral types:

    • L4: @param adds a javadocarrow-up-right comment about the parameter.

    The issue is that we cannot define the methods unless we know what specific numeral type this class should implement; in other words, it is too abstract to define those methods. Thus, we need to declare Numeral as a type of abstract class.

    What are the advantages of havingNumeral as a super class of all numeral types?

    There are two types of abstract classes in Java, abstract class and interface.

    Can an object be instantiated by an abstract class or an interface?

    hashtag
    Interface

    Let us define Numeral as an interfacearrow-up-right:

    • L2: abstract method

      • All methods in an interface are public that does not need to be explicitly coded.

      • Abstract methods in an interface are declared without their bodies.

    Who defines the bodies of the abstract methods?

    Let us create a new interface called SignedNumeral that inherits Numeral and adds two methods, flipSign() and subtract():

    Can an interface inherit either an abstract class or a regular class?

    • L1: extends inherits exactly one class or interface.

    • L9: default allows an interface to define a method with its bodyarrow-up-right (introduced in Java 8).

    Can we call add() that is an abstract method without a body in the default method subtract()?

    Although the logic of subtract() seems to be correct, n.flipSign() gives a compile error because n is a type of Numeral that does not include flipSign(), which is defined in SignedNumeral that is a subclass of Numeral.

    What kind of a compile error does n.flipSign() cause?

    There are three ways of handling this error: casting, polymorphism, and generics.

    hashtag
    Casting

    The first way is to downcast the type of n to SignedNumeral, which forces the compiler to think that n can invoke the flipSign() method:

    This removes the compile error; however, it will likely cause a worse kind, a runtime errorarrow-up-right.

    Why is a runtime error worse than a compile error?

    Downcastingarrow-up-right, although allowed in Java, is generally not recommended unless there is no other way of accomplishing the job without using it.

    How can downcasting cause a runtime error in the above case?

    hashtag
    Polymorphism

    The second way is to change the type of n to SignedNumeral in the parameter setting:

    This seems to solve the issue. Then, what about add() defined in Numeral? Should we change its parameter type to SignedNumeral as well?

    It is often the case that you do not have access to change the code in a super class unless you are the author of it. Even if you are the author, changing the code in a super class is not recommended.

    Why is it not recommended to change the code in a super class?

    How about we overridearrow-up-right the add() method as follows?

    • L2: @Override is a predefined annotationarrow-up-right type to indicate the method is overridden.

    The annotation @Override gives an error in this case because it is not considered an overriding.

    What are the criteria to override a method?

    When @Override is discarded, the error goes away and everything seems to be fine:

    However, this is considered an overloadingarrow-up-right, which defines two separate methods for add(), one taking n as Numeral and the other taking it as SignedNumeral. Unfortunately, this would decrease the level of abstraction that we originally desired.

    What are good use cases of method overriding and overloading?

    hashtag
    Generics

    The third way is to use genericsarrow-up-right, introduced in Java 5:

    • L1: T is a generic type that is a subtype of Numeral.

      • A generic type can be recursively defined as T extends Numeral<T>.

    • L2: T is considered a viable type in this interface such that it can be used to declare add().

    Can we define more than one generic type per interface or class?

    The generic type T can be specified in a subclass of Numeral:

    • L1: T is specified as SignedNumeral.

    This would implicitly assign the parameter type of add() as follows:

    The issue is that the implementation of add() may require specific features defined in the subclass that is not available in SignedNumeral. Consider the following subclass inheriting SignedNumeral:

    • L1: implements inherits multiple interfacesarrow-up-right.

    • L2-6: LongInteger is a regular class, so all abstract methodsarrow-up-right declared in the super classes must be defined in this class.

    Since the n is typed to SignedNumeral in L6, it cannot call any method defined in LongInteger, which leads to the same issue addressed in the casting section.

    Would the type of n being SignedNumeral an issue for the subtract() method as well?

    Thus, SignedNumeral needs to define its own generic type and pass it onto Numeral:

    • L1: T is a generic type inheriting SignedNumeral, that implies all subclasses of SignedNumeral.

    T can be safely passed onto Numeral because if it is a subclass of SignedNumeral, it must be a subclass of Numeral, which is how T is defined in the Numeral class.

    circle-info

    Generics are used everywhere in Java, so it is important to understand the core concept of generics and be able to adapt it in your code to make it more modular.

    hashtag
    Enum

    Let us create an enumarrow-up-right class called Signarrow-up-right to represent the "sign" of the numeral:

    • All items in an enum have the scope of staticarrow-up-right and the access-level of public.

    • Items must be delimited by , and ends with ;.

    The items in the enum can be assigned with specific values to make them more indicative (e.g., +, -):

    • L5: final makes this field a constant, not a variablearrow-up-right, such that the value cannot be updated later.

    • L8: this points to the objectarrow-up-right created by this constructor.

    • L11: @return adds a comment about the return value of this method.

    Why should the member field value be private in the above example?

    Note that value in L8 indicates the local parameter declared in the constructor whereas value in L13 indicates the member field declared in L5.

    hashtag
    Limit of Interface

    In SignedNumeral, it would be convenient to have a member field that indicates the sign of the numeral:

    • L2: All member fields of an interface are static and public.

    Can you declare a member field in an interface without assigning a value?

    Given the sign field, it may seem intuitive to define flipSign() as a default method:

    • L3: condition ? A : B is a ternary expressionarrow-up-right that returns A if the condition is true; otherwise, it returns B.

    Is there any advantage of using a ternary operator instead of using a regular if statement?

    Unfortunately, this gives a compile error because sign is a constant whose value cannot be reassigned. An interface is not meant to define so many default methods, which were not even allowed before Java 8. For such explicit implementations, it is better to declare SignedNumeral as an abstract class instead.

    hashtag
    Abstract Class

    Let us turn SignedNumeralarrow-up-right into an abstract classarrow-up-right:

    • L9: the default constructor with no parameter.

    • L17: another constructor with the sign parameter.

    • L10: this() calls the constructor in L17.

    Why calling this(Sign.POSITIVE) in L10 instead of stating this.sign = Sign.POSITIVE?

    • L29: abstract indicates that this is an abstract method.

    Member fields and methods in an abstract class can be decorated by any modifiers, which need to be explicitly coded.

    Is there anything that is not allowed in an abstract class but allowed in a regular class?

    In summary, SignedNumeral includes 2 abstract methods, add() inherited from Numeral, and multiply() declared in this class.

    Can you define an abstract class or an interface without declaring an abstract method?

    classarrow-up-right
    objectarrow-up-right
    Numeralarrow-up-right

    1.3. Unit Testing

    LongInteger: unit tests.

    hashtag
    Test: LongInteger()

    Let us create a testing class called LongIntegerTestarrow-up-right and make a unit testarrow-up-right for the constructors:

    • L6: the annotation indicates that this method is used for unit testing.

    • L7: methods used for unit testing must be public.

    • L8: tests the default constructor

      • : compares two parameters where the left parameter is the expected value and the right parameter is the actual value.

    • L12-15: tests the constructor with a string parameter.

    • L18-19: tests the copy constructor.

    When should we use import static instead of import?

    When you run this test, you see a prompt similar to the followings:

    hashtag
    Test: multiply()

    Let us define another method for testing the multiply() method:

    • L11-12: a can hold the integer 152415787517146788750190521 (), which is much larger than the maximum value of long that is .

    hashtag
    Test: compareTo()

    Let us define a method for testing the compareTo() method:

    • : passes if the parameter returns true.

    circle-info

    provides an effective way of ensuring the correctness of your program. Making a unit test for every method is standard practice, although this standard is often dismissed due to time pressure.

    package edu.emory.cs.algebraic;
    
    public class Numeral {
    }
    public class Numeral {
        /**
         * Adds `n` to this numeral.
         * @param n the numeral to be added.
         */
        public void add(Numeral n) { /* cannot be implemented */ }
    }
    public interface Numeral {
        void add(Numeral n);
    }
    public interface SignedNumeral extends Numeral {
        /** Flips the sign of this numeral. */
        void flipSign();
    
        /**
         * Subtracts `n` from this numeral.
         * @param n the numeral to be subtracted.
         */
        default void subtract(Numeral n) {
            n.flipSign();
            add(n);
            n.flipSign();
        }
    }
    default void subtract(Numeral n) {
        ((SignedNumeral)n).flipSign();
        add(n);
        ((SignedNumeral)n).flipSign();
    }
    default void subtract(SignedNumeral n) {
        n.flipSign();
        add(n);
        n.flipSign();
    }
    public interface SignedNumeral extends Numeral {
        @Override
        void add(SignedNumeral n);
        ...
    public interface SignedNumeral extends Numeral {
        void add(SignedNumeral n);
        ...
    public interface Numeral<T extends Numeral<T>> {
        void add(T n);
    }
    public interface SignedNumeral extends Numeral<SignedNumeral> {
        void flipSign();
    
        default void subtract(SignedNumeral n) {
            n.flipSign();
            add(n);
            n.flipSign();
        }
    }
    void add(SignedNumeral n);
    public class LongInteger implements SignedNumeral {
        @Override
        public void flipSign() { /* to be implemented */ }
    
        @Override
        public void add(SignedNumeral n) { /* to be implemented */ }
    }
    public interface SignedNumeral<T extends SignedNumeral<T>> extends Numeral<T> {
        void flipSign();
    
        default void subtract(T n) {
            n.flipSign();
            add(n);
            n.flipSign();
        }
    }
    public enum Sign {
        POSITIVE,
        NEGATIVE;
    }
    public enum Sign {
        POSITIVE('+'),
        NEGATIVE('-');
    
        private final char value;
    
        Sign(char value) {
            this.value = value;
        }
    
        /** @return the value of the corresponding item. */
        public char value() {
            return value;
        }
    }
    public interface SignedNumeral<T extends SignedNumeral<T>> extends Numeral<T> {
        Sign sign = Sign.POSITIVE;
        ...
    /** Flips the sign of this numeral. */
    default void flipSign() {
        sign = (sign == Sign.POSITIVE) ? Sign.NEGATIVE : Sign.POSITIVE;
    }
    public abstract class SignedNumeral<T extends SignedNumeral<T>> implements Numeral<T> {
        /** The sign of this numeral. */
        protected Sign sign;
    
        /**
         * Create a signed numeral.
         * the default sign is {@link Sign#POSITIVE}.
         */
        public SignedNumeral() {
            this(Sign.POSITIVE);
        }
    
        /**
         * Create a signed numeral.
         * @param sign the sign of this numeral.
         */
        public SignedNumeral(Sign sign) {
            this.sign = sign;
        }
        ...
        ...
        /** @return true if this numeral is positive; otherwise, false. */
        public boolean isPositive() {
            return sign == Sign.POSITIVE;
        }
    
        /** @return true if this numeral is negative; otherwise, false. */
        public boolean isNegative() {
            return sign == Sign.NEGATIVE;
        }
    
        /** Flips the sign of this numeral. */
        public void flipSign() {
            sign = isPositive() ? Sign.NEGATIVE : Sign.POSITIVE;
        }
        
        /**
         * Subtracts `n` from this numeral.
         * @param n the numeral to be subtracted.
         */
        public void subtract(T n) {
            n.flipSign(); add(n); n.flipSign();
        }
    
        /**
         * Multiplies `n` to this numeral.
         * @param n the numeral to be multiplied.
         */
        public abstract void multiply(T n);
    }
    import org.junit.jupiter.api.Test;
    import static org.junit.jupiter.api.Assertions.assertEquals;
    import static org.junit.jupiter.api.Assertions.assertTrue;
    
    public class LongIntegerTest {
        @Test
        public void testConstructors() {
            // default constructor
            assertEquals("0", new LongInteger().toString());
    
            // constructor with a string parameter
            assertEquals("12", new LongInteger("12").toString());
            assertEquals("34", new LongInteger("+34").toString());
            assertEquals("-56", new LongInteger("-56").toString());
            assertEquals("-0", new LongInteger("-0").toString());
    
            // copy constructor
            assertEquals("12", new LongInteger(new LongInteger("12")).toString());
            assertEquals("-34", new LongInteger(new LongInteger("-34")).toString());
        }
    }
    javadocarrow-up-right
    ≈287\approx 2^{87}≈287
    263−12^{63}-1263−1
    @Testarrow-up-right
    assertEquals()arrow-up-right
    assertTrue()arrow-up-right
    Unit testingarrow-up-right
    Tests passed: 1 of 1 test
    @Test
    public void testMultiply() {
        LongInteger a = new LongInteger("123456789");
    
        a.multiply(new LongInteger("1"));
        assertEquals("123456789", a.toString());
    
        a.multiply(new LongInteger("-1"));
        assertEquals("-123456789", a.toString());
    
        a.multiply(new LongInteger("-1234567890123456789"));
        assertEquals("152415787517146788750190521", a.toString());
    
        a.multiply(new LongInteger("0"));
        assertEquals("0", a.toString());
    
        a.multiply(new LongInteger("-0"));
        assertEquals("-0", a.toString());
    }
    @Test
    public void testCompareTo() {
        assertTrue(0 < new LongInteger("0").compareTo(new LongInteger("-0")));
        assertTrue(0 > new LongInteger("-0").compareTo(new LongInteger("0")));
    
        assertTrue(0 < new LongInteger("12").compareTo(new LongInteger("-34")));
        assertTrue(0 > new LongInteger("-12").compareTo(new LongInteger("34")));
    
        assertTrue(0 > new LongInteger("-34").compareTo(new LongInteger("12")));
        assertTrue(0 < new LongInteger("34").compareTo(new LongInteger("-12")));
    }