--- tags: fall 2018 cs61b --- # Classes and Interfaces Review [TOC] ## Abstract Classes The purpose of an abstract class is to have a class that cannot be instantiated. An abstract class can have abstract methods, but do not necessarily need them to be defined. This is useful because the abstract class is sometimes too vague to be instantiated by itself. Consider the abstract class `Shape` below. How do we know the sides and dimension of a shape? Well, we all know that shapes are two-dimensional. However, for sides, it really depends on what shape we are instantiating! Therefore, it would make more sense to not instantiate `Shape` and have it be a class that you can *inherit* methods from. **Example of Abstract Class** ```Java abstract class Shape { //an abstract method that returns the number of sides public abstract int sides(); /*This method is commented out because it would yield a compile-time error. Abstract methods cannot be static! However, you can have static concrete methods inside of an abstract class. static abstract int corners(): */ //prints out the dimension public void dimension() { System.out.println("I'm two-dimensional!"); } } ``` **Examples of Classes Extending Abstract Classes:** ```Java public class Square extends Shape { public int sides() { return 4; } } public class Triangle extends Shape { public int sides() { return 3; } } ``` Here are some examples of how methods would be run: ```Java Shape whatShape = new Shape(); //Compile-time Error -> Shape is an abstract class! Square box = new Square(); Triangle tri = new Triangle(); box.dimension(); //inherits the dimension method from Shape! box.sides(); //4 tri.sides(); //3 ``` ### Miscellaneous Abstract Class Facts - If an abstract class implements an interface, it does not need to implement any of the interface methods. However, any class that extends this abstract class will need to implement those same methods. ## Interfaces Interfaces are useful if you have many unrelated classes that want to implement the same methods in different ways. For example, `Square` and `Planet` from the code block below are competely unrelated classes, but they both want to be able to rotate. **Example of an Interface:** ```Java interface Rotatable { //fields are implicitly static or final final boolean canRotate = true; //a default method can have an implementation public default void spin() { System.out.println("I'm spinning!"); } //methods that would be implemented by concrete classes public void rotateLeft(); public void rotateRight(); } ``` **Examples of Implemented Interfaces:** ```Java public class Square implements Rotatable { public void rotateLeft() { System.out.println("CounterClockwise!"); } public void rotateRight() { System.out.println("Clockwise!"); } } public class Planet implements Rotatable { public void rotateLeft() { System.out.println("I'm not Venus or Uranus!"); } public void rotateRight() { System.out.println("I'm either Venus or Uranus!"); } } ``` ### Miscellaneous Interface Facts * Interfaces can **extend**, but **cannot implement** other interfaces. * If a concrete class implements an interface that extends another interface, the class must implement all methods in both of the interfaces. * Interfaces can be used as a static type when creating objects * Why is this nice? This way, `rotatableObj` can be instantiated as a `Square` or `Planet` object and pass the compiler. Otherwise, we must create a superclass that both `Square` and `Planet` can inherit from, which makes no sense since `Square` and `Planet` are not directly related classes. ```Java //The code below will pass the compiler and run! Rotatable rotatableObj; rotatableObj = new Square(); rotatableObj = new Planet(); ``` ## Abstract Classes Vs Interfaces **Similarities:** * Blueprints for a class * Both can have static methods * Abstract classes can simply have static methods * Interfaces can have static methods since Java 8 * Static methods cannot be overriden by methods in concrete classes * Concrete classes that implement the interface cannot use the static method * Both can have concrete methods * Abstract classes can simply have concrete methods * Interfaces use the **default** method since Java 8 * Note: default methods can be overridden in concrete classes **Differences:** * **Usage:** Abstract classes have an "is-a" relationship with other classes and are usually used as a broader descriptor of concrete classes. Interfaces are used to define what a class can do. It doesn't matter what type of class it is. It's actually common for interfaces to be named with the **-able** suffix. * Consider using abstract classes when: * You want to share code among several closely related classes. * You expect that classes that extend your abstract class have many common methods or fields, or require access modiers other than public (such as protected and private). * You want to declare non-static or non-final fields. This enables you to define methods that can access and modify the state of the object to which they belong. * Consider using interfaces when: * You expect that unrelated classes would implement your interface. For example, the interfaces `Comparable` and `Cloneable` are implemented by many unrelated classes. * You want to specify the behavior of a particular data type, but not concerned about who implements its behavior. * You want to take advantage of multiple inheritance of type. * **Fields:** Interface fields must be static or final. Abstract class fields are not limited in this sense. * **Extending/Implementing:** A concrete class can only extend one abstract class, but can implement multiple interfaces. * **Access control:** Interface methods are implicitly public while abstract class methods are implicitly package private (if you do not change the scope to private, public, or protected). An abstract method cannot be private. ## Data structures What are they? Data structures are concrete implementations of ADTs (Abstract Data Types). Funny enough, abstract data types are defined as blueprints for data structures. They contain methods and fields that would be implemented in a data structure! (This is why ADTs are interfaces) **ADTs:** Lists, Sets, Map, etc. **Data Structures:** ArrayLists, HashSets, HashMaps, TreeMaps, LinkedList, etc. ## Generics Generics provide a container that allow you to use *any type* with the class. In the example below, `T` is a *dummy variable* that represents a generic type. Note that you could have used any variable name that is not the name of an existing data structure such as `K`, `X`, `Y` to represent a generic type. It's common convention to name generic types as single uppercase letters! **Example of Class using Generic Type:** ```Java public class Container<T> { private T item; //sets item field to item of type T public void putItem(T item) { this.item = item; } //returns type T object public T getItem() { return item; } } ``` **Below are a few examples of how Container would work:** ```Java Container<String> c = new Container<String>(); //T will now refer to a String object c.putItem("cat"); c.putItem(new Point(3, 4)); //Compile-Time Error Container<Point> p = new Container<Point>(); //T will now refer to a Point object c.putItem(new Point(3, 4)); Container<T> t = new Container<String>(); //Compile-Time Error //T only exists inside the Container class so the symbol T would not exist Container<Animal> a = new Container<Cat>(); //Cat extends Animal //While Animal may be a superclass of Cat, Container<Animal> is not a superclass of Container<Cat>. Therefore, there would be a compile-time error. ``` ### Why use Generic types? A common question is why use a `Generic` type over an `Object` type. :::info Reminder: `Object` is a superclass of all of the classes in Java. The hierarchy looks like the picture below, but even bigger! Just know that all classes are extending `Object`. ![](https://i.imgur.com/Rs5VH60.png) This means you can run the code below and it would work! ```Java String s = "hi"; Object o1 = s; Integer i = new Integer(1); Object o2 = i; //Even custom (user-defined) classes work Container<String> c = new Container<>(); Object o3 = c; ``` ::: If `Object` can hold any kind of instance object, what's the point of generic types? Well, let's take a look at `Container` using `Object`. ```Java public class Container { private Object item; public void putItem(Object item) { this.item = item; } public Object getItem() { return item; } } ``` With `Object`, we could have `Container` hold any object! `Item` could be `Object`, `String`, or `Boolean` etc. ```Java Container c = new Container(); c.putItem("hi"); //works fine c.putItem(new Integer(1)); //works fine ``` However, what if we wanted `Container` to only hold `String` objects as the item? The code above wouldn't work because any instance object from a class can fit into `Object`! Therefore, we use a generic type. **Throwback to original code:** ```Java public class Container<T> { private T item; //sets item field to item of type T public void putItem(T item) { this.item = item; } //returns type T object public T getItem() { return item; } } ``` Now we run the code below: ```Java Container<String> c1 = new Container<>(); //specifies T to be String! //For this particular instance object, T = String! c1.putItem("hi"); //works fine! c1.putItem(new Integer(1)); //compile-time error //Container now only takes in Strings, not any other object! ``` ### Method Header Syntax *Note: References Iterators as an example. Read this only if you understand Iterators and/or have read the Iterator section.* We use Generic types used in our method signatures in different ways: **1. Interfaces Implemented by a Class Without Generic Types** To be able to address different types in classes that implement them, interfaces often use Generic types. An example is the `Iterator` interface. We use the `Iterator` interface for a variety of objects, so it's best for the `Iterator` interface to use the Generic Type `T`. ```Java //Iterator interface header contains a Generic type interface Iterator<T> //when implemented, the Generic Type is replaced with a concrete type class StringListIterator implements Iterator<String> { String current; //rest of the implemented methods (not written) } ``` **2. Interfaces Implemented by a Class With Generic Types** Banking off the example above, what if instead of `StringList`, we wanted to iterate through a list with a Generic Type? Consider the implementation below. ```Java class GenericList<T> implements Iterable<T>{ ArrayList<T> arr = new ArrayList<>(); void add(T item) { arr.add(item); } GenericListLiterator<T> iterator() { return new GenericListIterator<T>(); } //This class header has two references to Generic Types class GenericListIterator<T> implements Iterator<T> { T item; //implementation of GenericListIterator } } ``` More importantly, look at the class header of `GenericListIterator`. ``` Java class GenericListIterator<T> implements Iterator<T> ``` We need both of the `<T>` references because `GenericListIterator` will be directly referencing whatever object is represented by `T` in its implementation. **3. Method Generics** (Out of Scope) ```Java public static <R extends Comparable<R>> Comparator<T> getComp(Function<T, R> getter) ``` ``<R extends Comparable<R>>`` is called a method generic. Pulled from Summer 2018 CS 61BL Lab: > A method generic declares a generic type local to a single method (rather than the entire class), and can be used throughout the method similar to generics in classes. The method generic goes after the visibility keyword, public, and before the return type declaration. Additionally, there is a bound on the generic type R, stating that only types that extend Comparable<R> will be accepted by the compiler. ## Iterables and Iterators The concept of iterators in CS 61B is very similar to that of iterators in CS 61A! With iterators, you are able to iterate through the elements of a data structure (denoted as an iterable). ### What is the difference between an iterable and iterator? An **iterable** is a data structure that can be iterated through. Examples are arrays, `Linkedlists`, and `Arraylists`. An iterable will *implement* an `Iterable` interface. An **iterator**, on the other hand, is a data structure that acts as a tool, allowing you to iterate through an iterable. An iterator will *implement* an `Iterable` interface. ### Why are iterators useful? With iterators, we can use **for-each loops** for arrays of any object. In the example below, we use an **int** **array**. ```Java int[] arr = {1, 2, 3}; //the array we want to iterate through //(arr is an iterable) //normal for loop (more code to handle indexing) for (int i = 0; i < arr.length; i++) { System.out.println(i); } //for-each loop (looks much nicer!) for (int i: arr) { System.out.println(i); } //what the for-each loop is actually doing Iterator arrIterator = arr.iterator(); while (arrIterator.hasNext()) { System.out.println(arrIterator.next()); } ``` The normal **for loop** requires more methods regarding the data structure we're iterating through while the **for-each loop** implicitly uses an iterator that handles all the specifics *under the hood*. An iterator can eliminate an extra layer of abstraction and make your life much more convenient! ### Why are Iterable and Iterator interfaces? It also makes sense for `Iterator` to be an interface because many *unrelated* data structures should have a way of iterating through themselves. The same reason applies to `Iterable`. ### Making a Class Iterable Most existing data structures already implement the `Iterable` interface, so we can simply call their **iterator** method. However, if you wrote a custom class and you want to make it iterable, you have to have it implement `Iterable`. **Iterable Interface:** ```Java interface Iterable<T> { Iterator<T> iterator(); } ``` The `Iterable` interface requires you to return an `Iterator` object based on the the class. Therefore, we need to write an `Iterator` class that implements the `Iterator` interface. **Iterator Interface:** ```Java interface Iterator<T> { //tells us if there are more objects to iterate over boolean hasNext(); //tells us the object that will be iterated over T next(); } ``` **Example of a Class Implementing Iterable:** ```Java import java.util.ArrayList; import java.util.Iterator; //example of a class implementing Iterable public class StringList implements Iterable<String> { private ArrayList<String> words = new ArrayList<>(); public void add(String s) { words.add(s); } @Override //implemented method from Iterable interface public Iterator<String> iterator() { returns new StringListIterator(); } //nested class that implements Iterator class StringListIterator implements Iterator<String> { private int counter = 0; @Override //implemented method from Iterator interface boolean hasNext() { return counter < words.size(); } @Override //implemented method from Iterator interface String next() { String word = words.get(counter); counter++; return word; } } } ```