LECTURE 5 NESTED CLASSES ABSTRACT CLASSES INTERFACES ADAM
LECTURE 5 NESTED CLASSES ABSTRACT CLASSES INTERFACES ADAM SOLTESZ Adam_Soltesz@epam. com APRIL 03, 2018 – UNIVERSITY OF NYÍREGYHÁZA
Nested classes • Classes can be defined inside other classes. public class Outer. Class { class Nested. Class { } }
Types of nested classes • Nested classes can be static and non-static. • A nested class that is static is (not surprisingly…) called a static nested class. • A non-static nested class however, is called an inner class. public class Outer. Class { class Inner. Class { } static class Static. Nested. Class { } }
Nested class visibilities • Nested classes can be used with the same visibilities as fields or methods: • public • protected • <default> (package private) • private
Static nested classes • Static nested classes work as a regular member of the outer class. • There is no connection between the fields/methods of the static nested class and the outer class. • Instantiating a static nested class looks like the following: Outer. Class. Static. Nested. Class static. Nested. Class = new Outer. Class. Static. Nested. Class();
Inner classes • Inner class instances are always tied to an outer class instance. • They cannot exist without first creating an instance of the outer class. • Any number of inner class instances can be tied to the same outer class. • They have access to all outer class members (even if they are private). • They cannot be instantiated in a static method of the outer class. Outer. Class outer. Class = new Outer. Class(); Outer. Class. Inner. Class inner. Class = outer. Class. new Inner. Class(); public class Outer. Class { public void do. Something() { Inner. Class inner. Class = new Inner. Class(); } class Inner. Class { } }
Inner classes - example • Example for accessing the outer class’s fields in an inner class: public class Outer. Class { private String outer. Class. Field; class Inner. Class { public void print. Outer. Class. Field() { System. out. println(outer. Class. Field); } } }
Inner classes - shadowing • Shadowing happens when an inner class member has the same name as an outer class member: public class Outer. Class { private String text; class Inner. Class { private String text; public void print. Outer. Class. Field() { System. out. println(this. text); System. out. println(Outer. Class. this. text); } } }
Why use nested classes? • Some classes are only used in one class. This is a good opportunity to create a nested class. • Good way to logically group classes. • It can lead to more readable and more maintainable code. • But… • Don’t overuse it! Nested classes are used quite rarely.
Abstract classes • A class is abstract if it has at least one abstract method. • An abstract method is a method that does not have a body (implementation). • You can use the abstract keyword to mark a class / method abstract. • A class must be abstract if it contains at least one abstract method. • You can extend a class and provide an implementation to it’s abstract methods. • An abstract class cannot be instantiated. You have to implement all methods in a class if you want to create an instance. • An abstract method cannot be final.
Abstract classes - example public abstract class Shape { private int x, y; public void move. To(int x, int y) { this. x = x; this. y = y; } public abstract void draw(); } public class Circle extends Shape { @Override public void draw() { System. out. println("Drawing a circle. . . "); } }
Abstract classes – when to use? • Use an abstract class when you would like to create a parent class that has some method implementations but it’s child classes will define the rest of the methods. • Supports code reuse. • Abstract classes can be used as a type anywhere, but they cannot be instantiated.
Interfaces • Interfaces are similar to classes, but represent a higher abstraction level. • They can contain • constants, • abstract methods (methods without method body), • static methods, • default methods. • Interfaces can be extended by other interfaces. • Interfaces can be implemented by classes.
Interfaces - example public interface Printable { String HEADER = "--- EXAMPLE APP ---"; void print(); } public interface User. Service { int count. Users(); void delete. User(int user. Id); }
Interfaces - constants • Interfaces cannot contain all kinds of fields. • They can only contain public constants (static, final). • No modifier needs to be provided, they are added automatically. public interface User. Service { int STARTING_ID = 1; String DEFAULT_NAME = "John. Doe"; }
Interfaces – abstract methods • Interfaces can only contain instance methods that are abstract, so their body is not provided. • This way you are just providing a “contract” and the implementing class will give the concrete implementation. • All methods are public and abstract by default, you don’t need to declare it. public interface User. Service { int count. Users(); void delete. User(int user. Id); }
Interfaces – static methods • Interfaces can contain public static methods. • There methods can only be public in Java 8. They are public by default. • They cannot be abstract (static methods can never be abstract!). public interface Example. Interface { static void say. Hi() { System. out. println("hi"); } }
Interfaces – default methods • Interfaces can contain default methods. • These are instance methods that have a body (default implementation). • They can be overridden by implementing classes, but it is not mandatory. • They can call any other method in the interface. • Added for backward compatibility when extending the interface. public interface Example. Interface { default void say. Hi() { System. out. println("hi"); } }
Inheritance between interfaces • Interfaces can extend each other (like classes). • The big difference is that an interface can extend any number of interfaces (multiple inheritance). • If the same default method is coming from two parents, you must redefine the default method in the child (compiler cannot decide which one to use). public interface Example. Interface extends Cloneable, Auto. Closeable { }
Implementing interfaces • Interfaces can be implemented by classes. • Any number of interfaces can be implemented. • The class needs to provide and implementation (method body) for each abstract method coming from the interface(s).
Implementing interfaces - example public class User. Service. Impl implements User. Service { @Override public int count. Users() { return 0; } public interface User. Service { int count. Users(); void print. User. Details(); @Override public void print. User. Details() { System. out. println("Dummy user details"); } } }
Implementing interfaces - example public interface User. Counter. Service { public interface User. Printer. Service { int count. Users(); } void print. User. Details(); } public class User. Service. Impl implements User. Counter. Service, User. Printer. Service { @Override public int count. Users() { return 0; } @Override public void print. User. Details() { System. out. println("Dummy user details"); } }
Marker interfaces • Interfaces can have zero methods. • An interface that is completely empty is called a marker interface. • We implement it in a class if we want to signal that the class has some capability, without adding any new methods. • Example: Serializable
Why use interfaces? • Interfaces provide a contract that all implementing classes have to satisfy. • They specify WHAT your class can do and not HOW it can do it. • You can use the interface type in your code and assign an instance of an implementing class. • You can achieve a - sort of - multiple inheritance that is not possible between classes. • Program to interfaces and not to implementations: Your code should not depend on concrete implementations, you must let the implementing class of the interface to decide freely how it will perform the operation.
Interfaces – Java 9 • Private instance methods can be added. • Private static methods can be added. • (Private methods must contain body, they cannot be abstract, because there would be no way to override them. )
Sorting in Java • No need to reinvent the wheel, sorting algorithms are already implemented. • Arrays. sort() for arrays. • Collections. sort() for lists. • Primitives can be sorted by default. • For objects, you need one of the following things: • Implement the Comparable interface. • Create a new class that implements the Comparator interface.
Sorting primitives • Using the Arrays. sort() method you can sort primitives in ascending order. • No extra code is required. public static void main( String[] args ) { int[] numbers = new int[] {4, 9, 1, 2}; Arrays. sort(numbers); System. out. println(Arrays. to. String(numbers)); }
Sorting objects using Comparable • If a class implements the Comparable interface, it can tell about itself how it’s instances can be sorted. • A compare. To(o) method needs to be implemented. • The general contract of Comparable. compare. To(o) is to return • a positive integer if this is greater than the other object. • a negative integer if this is lower than the other object. • 0 if this is equals to the other object.
Comparable - example public class Car implements Comparable<Car> { private int speed; public Car(int speed) { this. speed = speed; } @Override public int compare. To(Car o) { if (this. speed > o. speed) { return 1; } else if (this. speed < o. speed) { return -1; } else { return 0; } } @Override public String to. String() { return "Car{speed=" + speed + '}'; } } public static void main( String[] args ) { Car[] cars = new Car[] {new Car(5), new Car(10), new Car(1)}; Arrays. sort(cars); System. out. println(Arrays. to. String(cars)); }
Comparable – example (simplified) public class Car implements Comparable<Car> { private int speed; public Car(int speed) { this. speed = speed; } @Override public int compare. To(Car o) { return Integer. compare(this. speed, o. speed); } @Override public String to. String() { return "Car{speed=" + speed + '}'; } } public static void main( String[] args ) { Car[] cars = new Car[] {new Car(5), new Car(10), new Car(1)}; Arrays. sort(cars); System. out. println(Arrays. to. String(cars)); }
Sorting objects using Comparator • Create a class that implements the Comparator interface. • A compare(o 1, o 2) method needs to be implemented. • The general contract of Comparator. compare(o 1, o 2) is to return • a positive integer if o 1 is greater than o 2. • a negative integer if o 1 is lower than o 2. • 0 if o 1 is equals to o 2. • Your comparator class can be passed as parameter to sorting algorithms.
Comparator – example public class Car { private int speed; public Car(int speed) { this. speed = speed; } public static void main( String[] args ) { Car[] cars = new Car[] {new Car(5), new Car(10), new Car(1)}; Arrays. sort(cars, new Car. Comparator()); System. out. println(Arrays. to. String(cars)); } public int get. Speed() { return speed; } @Override public String to. String() { return "Car{speed=" + speed + '}'; } } public class Car. Comparator implements Comparator<Car> { @Override public int compare(Car o 1, Car o 2) { return Integer. compare(o 1. get. Speed(), o 2. get. Speed()); } }
Questions? • Thank you for your attention, any questions?
- Slides: 33