home

Class Design

Abstract Classes

abstract class A {
    final void foo() {}  // This is fine
}

Packages

import is not needed for classes in same package.

package kt;

class Foo {
    static void foo() {}
}

package kt;

class Bar {
    // importing Foo is not needed
    void bar() {Foo.foo();}
}

Encapsulation

You can access a private attribute of another instance of same class within the class itself.

class Bar {
    private int bar;
}

class Foo extends Bar {
    private int foo;
    void foo(Foo foo) {
        this.foo = foo.foo;
        // This will not compile
        // this.foo = foo.bar;
    }
}

Inheritance

Inheritance in Instance Fields

There is no such thing as inheritance in instance fields.

class A {
    String a = "a";
}

class B extends A {
    String a = "b";
}

A a = new A();
A b = new B();

a.a; // a
b.a; // a

new A().a; // a
new B().a; // b

instanceof

If T is instanceof S, T[] is instanceof S[].

new Integer() instanceof Object;    // true
new Integer[0] instanceof Object[]; // true

Method Name Conflict Between Static and Instance Methods in Inheritance

class Foo {
    static void foo() {}
}

class Bar extends Foo {
    // This will not compile
    // void foo() {}
    // If Foo.foo were private, it would have been fine
    
    
    // Remember that this is fine
    static void foo();
}

Field Name Conflict in Inheritance

There is no such things as field name conflict in inheritance.

class A {
    int a = 42;
    static int b = 42;
}
class B extends A {
    int a = 84;  // compiles fine
    int b = 84;  // compiles fine
}

Overriding in Inheritance Trees

Overriding Instance Methods

An overriding method can not have a more restricting access than the method being overridden.

class Foo {
    protected void foo() {}
}

class Bar extends Foo {
    // This will not compile since default is more rectricting than proctected
    // void foo() {}
}

Overriding Constructors

An overriding constructor can have a more restricting access than the method being overridden.

class Foo {
    protected Foo() {}
}

class Bar extends Foo {
    // This is fine
    private Bar() {}
}

Overriding Static Methods

Static methods can not be overriden, but can be hidden

class A {
    static void foo() {}
}

class B extends A {
    static void foo() {}  // Hides A.foo
}

Assigning a weaker access in the hiding method is a compilation error

class A {
    static void foo() {}
}

class B extends A {
    // This will not compile!
    // private static void foo() {}
}

Hiding a final static method is a compilation error

class A {
    static final void foo() {}
}

class B extends A {
    // This will not compile
    // static void foo() {}
}

Patterns

Singleton Class

class MySingleton {
    // The single instance
    private final static MySingleton instance = new MySingleton();

    // Must hide the default constructor
    private MySingleton();

    // Factory method to get the only instance
    public static MySingleton getInstance() {
        return instance;
    }
}

Immutable Class

// class itself is final
final class MyImmutableClass {
    // All fields private and final
    private final int val;
    private final List<Integer> ints;

    // initalised in constructor
    public MyImmutableClass (int val, List<Integer> ints) {
        this.val = val;
        this.ints = ints;
    }

    // getters return copy
    public int getVal() {
        return this.val;
    }
    public List<Integer> getInts() {
        return new ArrayList<>(ints);
    }

    // no setters
}

Nested Classes

Static Nested Classes

class A {
    static class B {
    }
}
A.B ab = new A.B();
class A {
    int a;
    static class B {
        // This will not compile
        // int b = a;
    }
}
class A {
    private static class B {
        public void a(){};
        private void b(){};
    }
    B getB() {
        B b = new B();
        b.a();
        b.b(); // accessing a private method of a static nested class
        // No one can do anything with this reference except A itself
        return b;
    }
}

// This is fine but useless
new A().getB();

// This will not compile
// A.B b = new A().getB();

// This will not compile either
// new A().getB().a();

Inner Classes

class A {
    class B {
    }
}
A.B ab = new A().new B();
class A {
    private int a;
    private void a() {}
    class B {
        int b = a;
        void b() { a(); }
    }
}
class A {
    class B {
        // This is fine:
        static final int y = 10;
        // This will not compile:
        // static int x = 10;
    }
}

Local Classes

class A {
    void a() {
        class B {}
    }
}
class A {
    void a(int x) {
        int y = -1;
        class B {
            // This is fine
            int z = x + y;
        }
    }
}

Anonymous Classes

interface A {}
class B {
    A a = new A(){};
}

Anonymous Classes can capture values from surroundings

interface A {
    int a ();
}

class B {
    int b = 42;
    A a = new A() {
        @Override
        public int a() {
            return b;
        }
    };
}

Above example can be written as

interface A {
    int a ();
}

class B {
    int b = 42;
    A a = () -> b;
}

equals(Object) and hashCode()

Examples

Nested Class Examples

class Foo {
    class Bar {
    }

    static class Qux {
    }

    Bar fooBar() {
        Bar bar;
        bar = new Bar();
        bar = this.new Bar();
        return bar;
    }
}

class Baz {
    void baz() {
        Foo.Bar foobar = new Foo().new Bar();
        Foo.Qux qux = new Foo.Qux();
    }
}