diff --git a/Part 2.6 - Creating a Class.md b/Part 2.6 - Creating a Class.md index 6c4fb10010cb9cbc3a8b5a8bdc51beb9aa7e944d..07fa2870b4b81cc4f35e9f035389ce601393efcb 100644 --- a/Part 2.6 - Creating a Class.md +++ b/Part 2.6 - Creating a Class.md @@ -494,4 +494,5 @@ As an additional exercise, you may try now implementing the phonebook class full One additional benefit in using interfaces is that it reduces the need to manually write code, as the method signature's etc. can be automatically generated/copied with modern IDE's. -Now do the exercises of the Part 2 in ViLLE. \ No newline at end of file +Now do the exercises of the Part 2 in ViLLE and continue to Part 3: +[Part 3.1 - Inheritance](Part%203.1%20-%20Inheritance.md) \ No newline at end of file diff --git a/Part 3.1 - Inheritance.md b/Part 3.1 - Inheritance.md new file mode 100644 index 0000000000000000000000000000000000000000..d4093346359475f60b83de8938f9c4a686838c01 --- /dev/null +++ b/Part 3.1 - Inheritance.md @@ -0,0 +1,387 @@ +# Inheritance + +Today's Topics: +- Inheritance +- Philosophy of Inheritance +- Utilization of Inheritance +- Inheritance and Contract-based Programming +- Characteristics of Inheritance +- Inheritance vs Composition + +## Philosophy of Inheritance +Inheritance is one of the "cornerstones" of object-oriented programming, enabling selective reuse of code and describing relationships between classes. + +Two sides of inheritance: + +- **Internal**: the inheriting class incorporates as part of itself and thus gains access to the internal functionality of the inherited class. +- **External**: the inheriting class also inherits all public interfaces of the inherited class, which obliges the inheritor. + +Common challenges of inheritance: +- When to use inheritance? +- What can be achieved? +- How to support software project goals? +- What kind of conceptual structure follows from the inheritance relation? + +Using inheritance might seem simple, but in reality utilizing inheritance smartly can be challenging. And, as is often in programming, there often does not exist one true solution. However, there are some typical ways how inheritance can be utilized. They are the following: + +1. **Specialization** +2. **Detailing** +3. **Merging** +4. **Implementation Inheritance** +5. **Ad hoc Inheritance** + +The last two mentioned ways are somewhat against the "philosophy" of inheritance and hence less recommended. + +Next, let's look at typical ways of utilizing inheritance through examples. + +### 1. Specialization +- Represents a concept hierarchy as a class hierarchy. +- A top-level concept can be divided into more than one subclass. A "traditional" example could be `Animal` class which extends to multiple types of animals, e.g. cats, dogs or squirrels. Below is a more generic example of a list. + +```java + +class List { // common form of lists + Object getValue() { ... } // recursive structure + List successor() { ... } // successor = list object / null +} + +class WritableList extends List { // also supports setting values + void setValue(Object obj) { ... } +} + +class BidirectionalList extends List { // also supports returning to predecessor + List predecessor() { ... } +} +``` +Other example utilizing specialization: + +```java + +enum Action { + Ask, Print +} +``` +```java +void do(Action a) { + switch(a) { + case Ask -> + new Scanner(System.in).next(); + case Print -> + System.out.println("Hello"); + } +} +``` +```java +do(action); +``` + +The same example using an interface: +```java +interface Action { + void do(); +} + +class Ask extends Action { + @Override void do() { + new Scanner(System.in).next(); + } +} + +class Print extends Action { + @Override void do() { + System.out.println("Hello"); + } +} +``` +```java +action.do(); +``` +### 2. Detailing +The subclass concept is the same as the superclass concept. Unimplemented features are specified at the subclass level. (*template method* design pattern) Use of the `abstract` keyword before method and class definitions. + +Unimplemented methods must always be **abstract**. If the aforementioned methods are defined, the class must also be **abstract**. The class can also be abstract if direct instantiation is to be prevented (`new Class(..)`). An example using detailing (abstract) can be seen below: + +```java + +abstract class Renderer { + abstract void drawLine(Point p, Point p2); // Note! No implementation! + void drawTriangle(Point[] p) { + drawLine(p[0],p[1]); drawLine(p[1],p[2]); drawLine(p[2],p[0]); + } +} + +class JavaFX_Renderer extends Renderer { + @Override void drawLine(Point p, Point p2) { ... } +} +``` + +### 3. Merging + +Merging means combining multiple roles. The refined concept may appear in multiple roles compared to the superclass. Java limits inheritance to one direct superclass, but a class can implement multiple different interfaces. An example below: + +```java + +abstract class Human { // not all humans are + void eat() { ... } // musicians or teachers + void breath() { ... } +} + +interface Musician { // default implementation (overridable) + default void play(Song song) { System.out.println("Paranoid"); } +} + +interface Teacher { + void lecture(Subject s, Topic t); // responsibility of implementing class +} + +class RockTeacher extends Human implements Musician, Teacher { ...} +``` + +### 4. Implementation Inheritance + +The superclass may not have a direct relationship with the subclass. The aim is to avoid rewriting the same operations, and using them as tools. + +Note! Not a recommended approach at all, can lead to problems in the long run! E.g. due to uncoupled nature of superclass and subclass the methods implemented in the subclass may need to have "empty" implementation or raise exceptions etc. + +An example below: + +```java + +class BookShelf extends ArrayList<Book> { + List<Book> getAllFiction() { + var fiction = new ArrayList<Book>(); + for(var book: this) + if (book.isFiction()) fiction.add(book); + return fiction; + } +} +``` + +### 5. Ad hoc Inheritance + +Does not create a broader hierarchy, solves encountered problems with inheritance if it requires the least effort. + +Any problem you solve by using ad hoc inheritance should definitely be solved using other methods! + +An example below: + +```java + +class Translator extends java.util.Stack<Object> { + void randomFact() { + try { + pop(); + } + catch(EmptyStackException e) { // "Empty" + "StackException" + System.out.println("Empty is in English " + + e.getClass().getSimpleName().substring(0, 5)); + } + } +} +``` + +## Inheritance and Contract-based Programming + +## Characteristics of Inheritance: Reusability +- Objects share common characteristics + +1. defined for one object +2. other objects can inherit these characteristics + +- Class-based object-oriented programming (e.g., Java) + - subclass X, superclass Y (Java: **class X extends Y**), common characteristics in superclass + +```java + +class Student { // 1) + void attendCourse(Course c) { ... } // common +} + +class GraduateStudent extends Student { // 2) + void teachCourse(Course c) { ... } +} +``` + +```java +new Student().attendCourse(courseObject); +new GraduateStudent().attendCourse(courseObject); +new GraduateStudent().teachCourse(courseObject); +``` + +## Characteristics of Inheritance: Override +- The implementation of a method in the superclass can be overridden (Java: **@Override** recommended, but not mandatory) +- The method to be overridden is virtual (Java: everything except private, final, and static) + +```java + +abstract class Company { + Company(String name) { this.name = name; } + private String name; + abstract String slogan(); // purely virtual + String giveCompanyLogo() { return name; } // virtual + final String giveName() { return name; } // non-virtual +} + +abstract class StylishCompany extends Company { + @Override String giveCompanyLogo() { // overriding implementation + return " --={. " + giveName() + ".}=--"; + } +} +``` + +## Characteristics of Inheritance: Relations are Static +In Java, implementation and inheritance relationships are static. This means e.g. that the inheritance cannot be changed during run-time. Use of inheritance is unsuitable if the property does not exist all the time. Additionally, once inherited, the property cannot be hidden. It is difficult to know whether to inherit or not in advance. + +A possible workaround to allow more dynamic approach is the following: First provide functionality **statically**, and then **dynamically** choose what to do. + +Example below: + +```java + +interface FlightCapable { + void fly(); +} + +class OlliSquirrel implements FlightCapable { + private boolean flightCapable; // this value can be changed dynamically + @Override void fly() { if (flightCapable) /* ... */ } +} +``` + + +## Examining Class Hierarchy +**Bottom-up**: +- An employee is a person with specific characteristics that not all individuals possess. +- For an employee, it is sufficient to define new specific characteristics and inherit the rest from the person type. +- Characteristics should be described in the most general class possible. + + + +**Top-down**: +- Generally, we may know that concrete employee objects with defined workplaces can travel to their workplaces. +- Thus, an employee's weekly schedule can be defined abstractly based on daily tasks without knowing the exact job description or workplace location. + + + + +## Inheritance vs Composition + +Reusability is also possible through composition/delegation. Call syntax using composition is longer, but interface commitments are more freely selectable. + +Let's at first look an example utilizing inheritance: + +```java + +interface Stack { + void add(Object item); + Object remove(); +} + +// Also binds to LinkedList +class MyStack extends LinkedList<Object> implements Stack { + void add(Object o) { add(o); } + Object remove() { return removeLast(); } +} +``` +And the same example implemented utilizing composition: + +```java + +interface Stack {...}// same as before + +class MyStack implements Stack { + private List<Object> list = new LinkedList<>(); + void add(Object o) { list.add(o); } + Object remove() { return list.removeLast(); } +} +``` + +Next, lets look at how the implementation could be done, if the we also needed to implemented a subclass called `DebugStack`. + +Utilizing inheritance: + +```java +interface Stack {...}// same as before +class MyStack extends LinkedList<Object> implements Stack {...} // Same as before + +// Both MyStack and LinkedList +class DebugStack extends MyStack { + @Override void add(Object o) { + System.out.printf("Adding %s", o); + super.add(o); + } + // Inherited remove() fits as is +} +``` +And the same implementation utilizing composition: + +```java +interface Stack {...}// same as before +class MyStack implements Stack {...} // same as before + +class DebugStack implements Stack { + private Stack mine = new MyStack(); + @Override void add(Object o) { + System.out.printf("Adding %s", o); + mine.add(o); + } + @Override Object remove() { return mine.remove(); } +} +``` +### Couple More Examples of Inheritance vs. Composition Using Kotlin + +Delegation using the `by` keyword (Kotlin): + +```kotlin + +interface ActiveObject { + fun toggleState() + fun printState() +} +class SimpleActiveObject : ActiveObject { + private var state = false + override fun toggleState() { state = !state } + override fun printState() { println(state) } +} +class GameObject : ActiveObject by SimpleActiveObject() { + private var state = "ok" + fun gameMethod() { println(state) } +} +``` + + +Composition: state management delegated to SimpleActiveObject: + +```kotlin + +interface ActiveObject { ... } // same as before +class SimpleActiveObject : ActiveObject { ... } // same as before +class GameObject : ActiveObject by SimpleActiveObject() { + private var state = "ok" + fun gameMethod() { println(state) } +} +val gameObject = GameObject() +gameObject.toggleState() // SimpleActiveObject().state <- true +gameObject.printState() // prints "true" +gameObject.gameMethod() // prints "ok" +``` + + +Inheritance: state is within the GameObject: + +```kotlin + +interface ActiveObject { ... } // same as before +open class SimpleActiveObject : ActiveObject { ... } // same as before +class GameObject : SimpleActiveObject() { + private var state = "ok" + fun gameMethod() { println(state) } +} +val gameObject = GameObject() +gameObject.toggleState() // gameObject.state <- true +gameObject.printState() // prints "true" +gameObject.gameMethod() // prints "ok" +``` + +Next, continue to: +[Part 3.2 - Polymorphism](Part%203.2%20-%20Polymorphism.md) \ No newline at end of file diff --git a/Part 3.2 - Polymorphism.md b/Part 3.2 - Polymorphism.md new file mode 100644 index 0000000000000000000000000000000000000000..78813f846bb0893050e20ebcc3d052b01c1f71cf --- /dev/null +++ b/Part 3.2 - Polymorphism.md @@ -0,0 +1,184 @@ +# Polymorphism + +Today's Topics: + +- Polymorphism +- Relation +- Usage +- Roles and Hierarchies +- Abstract Classes +- Interfaces + +## Polymorphism: Is-a Relationship + +Inheritance provides a means to **reuse** and **replace** functionality. Inheritance essentially just syntactically packages the way to achieve reusability, that is already possible by procedures, through selection logic determined by **dynamic binding**. Previously, references to inheriting objects were always through their concrete class type references. + +**Polymorphism** is defined as the ability to reference an object through the type references of its superclass as well. Thus, polymorphism establishes the *is-a* relationship. + - From the perspective of contracts and correctness: inheritance should be based on logical substitutability. + - An inheriting class can represent the inherited class in all respects, but not necessarily vice versa. + +## Polymorphic Handling of Values + +Let's look at an example describing a `Employee` and `Unemployed` classes which both extend `Person` class. + +```java +abstract class Person {} +class Employee extends Person {} +class Unemployed extends Person {} +Unemployed unemployed = new Unemployed(); +Employee worker = new Employee(); +Person someone; +``` +Using a UML you could describe the said class structure in the following way: + + + +Explicit (type) conversions: +```java +someone = (Person)unemployed; // arrow direction +unemployed = (Unemployed)someone; // against arrow +someone = (Person)worker; // arrow direction +worker = (Employee)someone; // against arrow +``` + +Conversions in the direction of the arrow work automatically! When we need to manually force the type conversion, there is a risk of failure. + +```java +someone = unemployed; // arrow direction +unemployed = (Unemployed) someone; // against arrow +someone = worker; // arrow direction +worker = (Employee) someone; // against arrow +``` +Next, we will look at a slightly modified example that highlights some of the limitations. + +```java +abstract class Person {} +class Employee extends Person {} +class Unemployed extends Person {} +Unemployed unemployed = new Unemployed(); +Employee worker = new Employee(); +Person someone = unemployed; // arrow direction +``` +Limmitations are that we cannot move: +1) both in the direction and against the arrow with the same conversion or + +2) without an arrow between the classes. + +```java +worker = (Employee)someone; // raises ClassCastException +worker = (Employee)unemployed; // compiler: incompatible types +``` + +## Class Hierarchy + +Now let's look at the this through the lens of class hieracrhy. + +Java: a class can inherit from at most one superclass. +- leads to class inheritance chains +- subclasses correspond (is-a) to their superclasses +```java +class Throwable { .. } +class Exception extends Throwable { .. } +class MyProblem extends Exception { .. } +``` +Notice how the `Exception` extends to `Throwable`. + +Using UML you could describe the above class structure in the following way: + + + +Generally applies +- a class can inherit one superclass +- a class can be inherited by several subclasses (1+) +- **final** = no subclasses +- **private** constructor, number of instantiations can be controlled by the class itself. + +For example how a private constructor could be used: a class could be restricted so that creating a new instance of a class using a `new` keyword would be impossible, but a `public static` method of the said class could call the private constructor. Even though that might sound confusing, there are some valid use cases. For example, Java enforces calling the parent class in the constructor first (`super()`), and there might be a case where something else would be preferred before doing so. + +We can also extend the hierarchy to display the `Object` class. In Java all class chains ultimately inherit from the `Object` class. + +So the inheritance is the following: + + + + +Java: + +```java +class Object { .. } +class Throwable extends Object { .. } +class Exception extends Throwable { .. } +class MyProblem extends Exception { .. } +``` +However, as this is general case for all classes, it is not necessary to mention the `Object` class. E.g. `class AwesomeObject extends Object` the `Object` is not necessary. + +Different chains of inheritance form a tree. It is important to note that we can polymorphically reference everything via the `Object` type and even enums inherit from objects despite using a different word in signature declaring the class (enum). + + + + +```java +class Object { .. } +class Throwable extends Object { .. } +class Exception extends Throwable { .. } +class MyProblem extends Exception { .. } +class String extends Object { .. } +// or just +class String { .. } +``` + + +## Abstract Classes in Class Hierarchy + +Abstract classes are positioned as intermediate classes in the hierarchy. They design goal is preventing direct or indirect (due to missing features) instantiation. + + + +```java +class Throwable { .. } +class Exception extends Throwable { .. } +class MyProblem extends Exception { .. } +class String { .. } +``` +```java +abstract class AbstractCollection { .. } +abstract class AbstractList extends AbstractCollection { .. } +class Vector extends AbstractList { .. } +``` + +A class cannot be declared `abstract` and `final` at the same time. You might wonder the differences between final and abstract classes. The difference between abstract and final is that object of abstract class can't be instantiated and class declared `final` can't be inherited. + +```java +final class Final { .. } +``` +## Interfaces in Class Hierarchy +Specification of an interface includes + - interface name + - public methods + - recursively corresponding upper interfaces + +**Class**: defines both the interface and its implementation while **interface**: defines only the interface. Furthermore, interface does not tie together into a class hierarchy and it cannot inherit from a class. An interface has no inheritable state (member variables). + +A class can implement (many) interfaces and an interface can extend another interface. + + + + +In Java, interfaces are always declared using a keyword `implements`, while a class inheritance is declared with the keyword `extends`. + +```java +class Throwable implements Serializable { .. } +class Exception extends Throwable { .. } +class MyProblem extends Exception { .. } +abstract class AbstractCollection { ..} +abstract class AbstractList extends AbstractCollection { .. } +class Vector extends AbstractList { .. } +class String implements CharSequence {..} +``` +Helper methods can be declared using (`default` & `static` Java8+, `private` Java9+) + +Due to interface limitations, implementation relies on public interface or global (`static`) state, not on member variable references. + + +Now continue to: +[Part 3.3 - Binding - Override and Overload - Exceptions](Part%203.3%20-%20Binding%20-%20Override%20and%20Overload%20-%20Exceptions.md) \ No newline at end of file diff --git a/Part 3.3 - Binding - Override and Overload - Exceptions.md b/Part 3.3 - Binding - Override and Overload - Exceptions.md new file mode 100644 index 0000000000000000000000000000000000000000..e32256226ac323d9a84700c9ec421268fae1110e --- /dev/null +++ b/Part 3.3 - Binding - Override and Overload - Exceptions.md @@ -0,0 +1,319 @@ +# Binding + +Today's Topics: + +- Binding + - Static and dynamic type + - Static binding + - Dynamic binding + +- Miscellaneous topics + - Override and overloading + - Error handling + +## Binding + +## Static and dynamic type + +Polymorphism creates the need to distinguish between the two types related to the structure of the language providing the reference: +- **Static type** + - Type mentioned in the signature of a variable, method return value or formal parameter + - Remains unchanged (when looking at the same reference structure – there can be different references to an object to which the referencing structure has a different static type). +- **Dynamic type** + - The concrete actual type of the object at the end of the reference. + - The type of an object that has already been created cannot change. + - When looking at the structure that provides a reference, you can refer to different dynamic types in different moments. + - Similarly, every reference to the same object has the same dynamic type. + +## Set of dynamic types + +- **Set of dynamic types** + - Formed by the reference’s static type and the derived types + - Each member compatible (substitution) with the static type + +```java +Employee person = new Supervisor("Pointy-haired boss"); +Developer dev = new Developer("Dilbert"); +``` + + + +## Upcasting and downcasting + +- Upcasting + - someone = employee +- Downcasting + - employee = (Employee)someone + - Not safe – may fail ⇒ ClassCastException + + + + +- reference `instanceof` type + - safe way – can be verified that conversion is ok. + +Below is an example of upcasting and downcasting. Downcasting is checked by `instanceof`. + +```java +class Person {} +class Employee extends Person { + void work() {} +} + +void main() { + Employee worker = new Employee(); + Person someone = worker; + + if (someone instanceof Employee) + worker = (Employee) someone; + + if (someone instanceof Employee e) + worker = e; +} +``` +## Binding + +- Class `A` and the subclass `B` give a different implementation for method `d`. +- Class `A`'s client calls the method d using the polymorphic reference of type `B` stored in variable `a`. + - dynamic type B, static type A + +- What do the calls print? + +```java +class A { + int s = 10; + int d() { return s; } +} +class B extends A { + int s = 20; + int d() { return s; } +} +``` + +```java +B b = new B(); +A a = b; +// __, __, __, __ +System.out.printf( +"%d, %d, %d, %d", +a.s, b.s, a.d(), b.d()); +``` + +## Option 1: static binding + +- Dispatch strategy chosen at compile time +- The compiler cannot know the dynamic type of the variable `a.s` +Type + - `a.s` calls the class `A` member variable +- Assumption e.g. in C++ for methods (programmer can change this) +- In Java, following are statically bound_ + - member variables + - `static` class methods + - `final` methods + - `private` methods + + +Below is a commented version of previous code that demonstrates how variable s can be defined multiple times. +```java +B b = new B(); +A a = b; +// 10, 20, __, __ +System.out.printf( +"%d, %d, %d, %d", +a.s, b.s, a.d(), b.d()); +``` + +## Option 2: Dynamic binding + +- Dispatch strategy chosen at run time +- Routine `d` is selected from the currently associated variable `a` by object type + - `a.d()` returns the class `B`'s member variable `s` +- Default binding in Java + - Not changeable by a programmer + - In Java, static binding is essentially an optimization in situations where the dynamic type is known. + +Below the comments have been added to show that a.d() returns also B's member variable s. +```java +B b = new B(); +A a = b; +// 10, 20, 20, 20 +System.out.printf( +"%d, %d, %d, %d", +a.s, b.s, a.d(), b.d()); +``` + +## When to use dynamic binding + +- You can see the way each object is looking for a way to react to a method call situation. +- The search is essentially a dynamic binding. The interface promises a `run` method. Even if the reference is from `Function` inteface, the right fun method is found. + +```java +enum Function { Ask, Print } +``` + +```java +void run(Function f) { + switch(f) { + case Ask -> + new Scanner(System.in).next(); + case Print -> + System.out.println("Hello"); + } +} +``` + +```java +interface Function { void run(); } + +class Ask extends Function { + @Override void run() { + new Scanner(System.in).next(); + } +} +class Print extends Function { + @Override void run() { + System.out.println("Hello"); + } +} +``` + +```java +f.run(); +``` + +- Combined with polymorphism and interface definition, an object can be handled more abstractly in different contexts through the roles they have, without the need to know concrete characteristics of the objects. + +```java +interface Stack { + void add(Object o); + Object remove(); + int size(); + + // Default implementation of an abstract type + default boolean isEmpty() { + return size() == 0; + } +} +``` + +```java +class MyStack implements Stack { + private List<Object> l = new ArrayList<>(); + public void add(Object o) { l.add(o); } + public Object remove() { l.remove(); } + public int size() { return l.size(); } + void add(Object[] os) { for(var o: os) o..} +} +``` + +```java +Stack p = new MyStack(); +p.add("item"); +System.out.println(p.isEmpty()); +``` + +# Miscellanous + +## Override and overload + +- In Java, syntactic solution: the method of the same name can either replace the previous one, given in the parent class.. +- .. or overload it (= many implementations with the same name) if the parameters of the method differ. +- Overload is addressed here as it is an integral part of basic Java and +confusingly similar looking. +- However, overload has nothing to do with inheritance or dynamic binding! + +- Arity: (1: unary, 2: binary, 3: ternary, .. ) + - void foo(T<sub>1</sub>a<sub>1</sub>, T<sub>2</sub>a<sub>2</sub>, ..., T<sub>n</sub>a<sub>n</sub>) +- Overloaded function: differs either in arithmetic or in data types of parameters, function name is the same + + +- **Override** of the superclass method in the subclass + - Call parameters compatible (in Java: same) with superclass + - The superclass method is no longer used in subclasses and subsubclasses. + - callable in subclass: `super.method` + +- Method **overload** + - Method call parameters differ + - It is always a new method, not a replacement for the old one (even when defined in a subclass) + - no dynamic binding is used (unless overload also replaces) + +## Polymorphism and exception treatment + +- **throws**: can only be raised from the dynamic type set of exception classes listed in the signature +- **catch**: The different catch branches must cover all types of exceptions mentioned in the signature. + - i.e. too broadly defined signature backfires every time on the processing side as unnecessary contingency! +- However, the `catch` branch is selected according to the dynamic type of the exception. +- Hierarchy of exceptions and exception types + - checked exceptions + - unchecked exceptions + +## Unchecked exception + +- no need to introduce in the signature +- not usually dealt with `catch` +• **Error**: serious errors +• **RuntimeException**: runtime error + +## Example: Unchecked exception + +```java +class Factory { + // native = external routine outside JVM + native static Object createObject(); +} +void main() { + // Reference outside of garbage collection + // Release must be handled manually + Object object = Factory.createObject(); + // null = could not allocate memory + if (object == null) + throw new RuntimeException("Out of memory!"); +} + +``` + +## Checked exeption + +- Must be mentioned in the signature (**throws**) +- Must be explicitly + - handled at use site (`catch`) and/or + - propagated +• Inherited from the **Exception** class + +Tree chart of different exceptions: + + +## Example: Checked exception + +```java +String read(String myPath) throws NoSuchFileException { + try { + // raise different (subclasses) IOException exceptions, see. Java API + return Files.readString(Path.of(myPath)); + } + catch(NoSuchFileException e) { + throw e; // propagates + } + catch(IOException e) { + System.out.println("Failed in other ways! Handling here."); + return null; + } +} +``` +Note! "Converting" an exception to null may not be a good idea.. + +```java +void logReader() { + try { + // can raise a NoSuchFileException + read("/var/log/apache.log"); + System.out.println("ok"); + } + catch(NoSuchFileException e) { + System.out.println("File is missing!"); // handled + } +} +``` + +Next topic is: +[Part 3.4 - Class Constructs](Part%203.4%20-%20Class%20Constructs.md) \ No newline at end of file diff --git a/Part 3.4 - Class Constructs.md b/Part 3.4 - Class Constructs.md new file mode 100644 index 0000000000000000000000000000000000000000..6b7a292a8f0d4c4c7b1167bd0a688588a98c76a9 --- /dev/null +++ b/Part 3.4 - Class Constructs.md @@ -0,0 +1,555 @@ +# Class constructs + +Today's Topics: + +- Java class constructs + - Basic class types + - Static Inner Class (Java 1.1+) + - Nested class (Java 1.1+) + - Anonymous class (Java 1.1+) + - Function literals and interfaces (Java 8+) + - Literal class (Java 5+) + - Record class (Java 16+) + - Closed class (Java 17+) + +## Basic class types in Java class mechanism + +The basic class constructs in Java (from version 1.0) are: +• (concrete) class +• abstract class +• interface (class) +We’ll dwelve more deeply in the class types in the context of inheritance + + +## Specifics of the class mechanism in Java + +Next, consider the types of special classes in Java: +1) **Static inner class:** syntactically related to its outer class +2) **Instance-specific inner class: (nested)** cannot exist without the instance of its outer class +3) **Anonymous class:** created on the fly for disposable use +4) **Function literals and interfaces:** interface: describes the function D → R, literal: implements the function +5) **Literal class:** the set of objects to be implemented is limited, defined in connection with the class +6) **Record:** non-polymorphic immutable data, value semantics +7) **Sealed class:** set of inheriting classes limited, no objects +8) **Hidden Class** (JEP 371, not covered) + +Next we will look at each of these classes in more detail. + +## Static Inner Class (Java 1.1+) + +Use purpose +- Brings together related classes "under one roof". +- **Is a means of encapsulation!** +- E.g. in the standard library `Entry` under `Map` classes (describes one key-value pair) . + +Syntax +- `static class`, inside the outer class (not inside a method). + + + +```java +class Outer { + static int x = 42; + static class Inner { + void setX() { + x = 100; + } + } + Inner object1 = new Inner(); +} +class Second { + Inner object2 = + new Outer.Inner(); +} +``` + +Features + +- Inner and outer classes are syntactically related. +- The inner class has no connection to the (outer classes) outer objects. +- The inner class and its inner objects can see inside the outer class (static features only). +- The outer class can see inside the inner class. +- The inner class is visible outwards according to visibility attributes. +- Inner classes defined in the superclass are also displayed in the subclasses (the same "inherited" inner class). +- Class `import` also applies to inner classes. +- Inner classes can also be assigned to inner classes without a limit. +- For the outer class and its objects, the inner class is unambiguous (e.g. In Scala language, its type depends on the outer object). + + +## Nested class (Java 1.1+) + +Use purpose + +- Objects in the inner and outer classes have an occurrence-specific relationship. +- Exterior state and operations available to inner objects. +- E.g. Event handling in user interfaces, network connection. + +Syntax +- **class**, definition within another class/method + + + +```java +class Outer { + int common = 42; + + class Inner { + int x = Outer.this.common; + Inner(int x) { + this.x = x; + } + void setCommon() { + common = x; + } + } +} +``` +Usage: +```java +new Outer().new Inner(); +``` + + +Features +- Creating an inner object requires an outer object to exist. +- The inner object has an implicit connection to the object of the outer class +(`Outer.this.x` or only `x` if unambiguous). +- There is no corresponding connection between the outer object and the inner object - if necessary, add the connection explicitly. +- Other visibility rules like for static indoor class. +- An instance-specific inner class can also be defined within the method (so-called local class). +- If access to the outer object is not required, static indoor class is more efficient (IDE can recommend). +- In a way, contrary to the principles of encapsulation! + +## Example: Web server + +A server that opens connections in an external object. Connection management in inner objects. + +```java +class Server implements Runnable { + ServerSocket sSocket; + boolean active = true; + + Server(int port) { /* ... */ } + class Handler implements Runnable { + Socket socket; + Handler() { try { socket = sSocket.accept(); } /* ... */ } + @Override public void run() { while(active) { /* ... */ } } + } + @Override public void run() { + while(active) new Thread(new Handler()).start(); + } +} +``` + +## Anonymous class (Java 1.1+) + +Use purpose +- Use combines object creation and class definition. +- When creating, you want to define new or +redefine previous functionalities. +- There is no desire to define a new specific class. + +Syntax +- **new Class(arguments) { definitions }** + + +```java +class A { void a() {} } +void main() { + // define the class & + // create an instance at the same time + A object = new A() { + @Override void a() { + System.out.println("Hello"); + } + }; +} +``` + +Features +- As a rule can be created anywhere inside the class - compares with the expression `new A()`. +- Anonymous class inherits the class mentioned in the definition and the created object is not its direct instance. +- Anonymous class is also **a instance-specific inner class**. +- Note! There is no access to completely new member definitions through named class/interface types. +- Can be expanded to instance-specific inner class if needed. + +## Example 1: Anonymous class + +First we give an example of a program with separate class definition and inheritance and then an example of the same program using an anonymous class. + +**Separate class** + +Auxiliary definition: + +```java +class A { void hello() {} } +``` + +Definition: + +```java +class Hello extends A { + @Override void hello() { + System.out.println("Hello"); + } +} +``` + +Usage: + +```java +new Hello().hello(); +``` + +**Anonymous class** + +Auxiliary definition: + +```java +class A { void hello() {} } +``` + +Definition: + +```java +// no separate definition +``` + +Usage: + +```java +new A() { + @Override void hello() { + System.out.println("Hello"); + } +}.hello(); +``` + +## Example 2: Anonymous class + +User interface handler as a class of its own. + +```java +void main() { + var window = new JFrame("Window"); + + class Adapter extends WindowAdapter { + @Override public void windowClosing(WindowEvent e) { + System.exit(0); + } + } + + window.addWindowListener(new Adapter()); +} +``` + +User interface handler as an anonymous class. + +```java +void main() { + var window = new JFrame("Window"); + + window.addWindowListener( + new WindowAdapter() { + @Override public void windowClosing(WindowEvent e) { + System.exit(0); + } + } + ); +} +``` + +In this example there is a benefit to not having to define a completely new class. That would introduce a lot of extra work that is not needed with an anonymous class. + +## Function literals and interfaces (Java 8+) + +Use purpose +- Interface definition: want to configure an interface with exactly one method. +- Function literal: You want to give the interface a +functional implementation. +- Example: interface describes the function D → R +- Literal produces a reference to an object of an anonymous class that implements interface **F**: + +Inteface definition: +```java +interface Function { + int count(int input); +} +``` +Usage: +```java +Function function = x -> x + 1; +``` + +Usage previously before lambdas: +```java +Function function = new Function() { + @Override int count(int x) { return x + 1; } +} +``` + +Features +- The function literal (lambda) is also an object of an anonymous instance-specific inner class. +- Java can apply literal to cases where either +the class has one undefined member or the class is +an interface defining a single method. + +More generic example of the syntax: + +Inteface definition: +```java +interface F<D, R> { + R count(D input); +} +``` +Usage: +```java +F<Integer, Integer> function = x -> x + 1; +``` + +```java +F<Integer, Integer> function = new F<>() { + @Override public Integer count(Integer x) { return x + 1; } +} +``` + +## Literal class (enum) (Java 5+) + +Use purpose +- Models data that has always an exact value from the values listed in the configuration (or null). +- Values (objects) are global constants, created during definition. +- A good choice if the list of values does not change in the future. - If there may be changes (changes would be propagated +to many places), one could think of an interface + +subclasses. + +Syntax +- enum Class { values listed; definitions } + +```java +enum Suit { + Heart, + Spade, + Club, + Diamond +} +``` + +Features +- More values cannot be listed without changing the code. +- Values can be parameterized in constructor arguments. +- Objects of the are immutable. +- Objects of the class are automatically assigned basic features, e.g. `equals` and `toString`. +- `==` and `equals` both work, `==` also works when the reference is `null`. +- All values as a list: `Suit.values()` +- `switch` statement can see if a value is left unused. +- Always inherits `java.lang.Enum`, cannot be replaced. + +Switch-statement: +```java +char symbol(Suit suit) { + return switch (suit) { + case Heart -> '♥'; + case Spade -> '♠'; + case Club -> '♣'; + case Diamond -> '♦'; + }; +} +``` + +## Example: Literal class +Enum syntax: +```java +enum Dicevalue { One, Two, Three, Four, Five, Six } +``` + +Imitation with a regular class (does not include all auxiliary routines): +```java +class Dicevalue { + private Dicevalue() {} + public static final Dicevalue One = new Dicevalue(); + public static final Dicevalue Two = new Dicevalue(); + public static final Dicevalue Three = new Dicevalue(); + public static final Dicevalue Four = new Dicevalue(); + public static final Dicevalue Five = new Dicevalue(); + public static final Dicevalue Six = new Dicevalue(); + public static final Dicevalue[] values() { + return new Dicevalue[] { One, Two, Three, Four, Five, Six }; + } +} +``` + +## Example: Bank account + +```java +class Bankaccount { + enum EventType { DEPOSIT, WITHDRAWAL }; + class Event { + final EventType eventType; + final long balanceChange; + Event(EventType type, long change) { + eventType = type; + balanceChange = change; + } + @Override public String toString() { + return number +":"+ eventType + "(" + balanceChange + ")"; + } + } + + private long number, balance = 0; + private final LinkedList<Event> events = new LinkedList<>(); + Bankaccount(long number) { this.number = number; } + void deposit(long amount) { + balance += amount; + events.add(new Event(EventType.DEPOSIT, amount)); + } + void withdrawal(long amount) { + balance -= amount; + events.add(new Event(EventType.WITHDRAWAL, amount)); + } +} +``` + +## Example: Playing card + +```java +class Card { + enum Value { + ACE("A", 1), TWO(2), THREE(3), FOUR(4), FIVE(5), SIX(6), SEVEN(7), EIGHT(8), NINE(9), TEN(10), JACK("J", 11), QUEEN("Q", 12), KING("K", 13); + + final String symbol; + final int numbervalue; + Value(String symbol, int value) { + this.symbol = symbol; numbervalue = value; + } + Value(int value) { + this("" + value, value); + } + } + + enum Suit { + CLUB("♣"), DIAMOND("♦"), HEART("♥"), SPADE("♠"); + final String symbol; + Suit(String symbol) { this.symbol = symbol; } + } + private Value value; + private Suit suit; + Card(Value value, Suit suit) { + this.value = value; this.suit = suit; + } + Value value() { return value; } + Suit suit() { return suit; } + @Override public String toString() { + return suit.symbol + value.symbol; + } +} +``` + +## Record class (Java 16+) + +Use purpose +- Model non-polymorphic immutable data. +- Value semantics: two objects are in a comparative sense "same" if their counterparts are the same. +- E.g. Serializable messages in a web service between frontend and backend sections. + +Syntax +- **record Class(parameters) { configuration }** +- The list of parameters of the constructor are directly after the class name. + +```java +record Element( + String name, + double atomicmass +) { +// Possible additional methods +} +``` + +Usage: +```java +var element = new Element("Oxygen", 15.9994) +``` + +Features +- Java pre-creates basic feature methods `toString`, `hashCode` and `equals`. +- Java also creates get methods for members (For member `a` named `a()`, not JavaBeans-style `getA()`). +- Difference to `enum` class: unlimited values can be created, +even after definition; values are of the same type. +- Previously: imitated with a final class with final members +and `hashCode` & `equals` that compare members in pairs. +- Always inherits `java.lang.Record`, cannot be replaced. + +## Example: Bank account + +By using record classes, the `Event` inner class of the `Bankaccount` class is further shortened if the use of unchanged values is accepted: + +```java +class Bankaccount { + enum EventType { DEPOSIT, WITHDRAWAL }; + record Event(EventType eventType, long balanceChange) { + // let's still change the toString implementation + // The default implementation doesn't look like what we want + @Override public String toString() { + return number +":"+ eventType + "(" + balanceChange + ")"; + } + } + // ... +} +``` + +## Example: Playing card + +Record classes also shorten the `Card` class if unchanged values are accepted: + +```java +record Card(Value value, Suit suit) { + enum Value { /*same as before*/ } + enum Suit { /*same as before*/ } + // The default implementation of Java still doesn't look like what we want + @Override public String toString() { + return maa().symboli + arvo().symboli; + } +} +``` + +## Sealed class (Java 17+) + +Use purpose +- A finer class definition capable of limiting inheriting classes. +- Inheriting classes either closed (recursively), **non-sealed**, or **final**. +- Enables e.g. algebraic data structures, e.g. tree structure with three classes: Tree, InnerNode, LeafNode + +Syntax +- `sealed class X permits Y, Z { Definitions }` + + +Hierarchy definition: +```java +abstract sealed class Shape permits Circle, Square, Triangle {} + +final class Circle extends Shape {} +final class Square extends Shape {} +final class Triangle extends Shape {} +``` + +Character adaptation: +```java +int angles(Shape shape) { + return switch (shape) { + case Circle c -> 0; + case Triangle t -> 3; + case Square r -> 4; + }; +} +``` + +Features +- Dedefinition character adaptation (switch) +- `Switch` statement can see if any branch of the sealed class remains unhandled. +- A subclass can also be `record` or `enum` if the superclass is `interface`. +- Difference to enum class: values can be created without +limit, even after definition; values of different branches +have different subtype but the same base type. +- Formerly: imitated (to a limited extent) with a static inner class with a `private` constructor. + + +Now it's time for Part 3 exercises in ViLLE. \ No newline at end of file diff --git a/images/part-10/nested.png b/images/part-10/nested.png new file mode 100644 index 0000000000000000000000000000000000000000..ee99472b66d5ff497220d7f9ffb4162250b5d446 Binary files /dev/null and b/images/part-10/nested.png differ diff --git a/images/part-10/staticinner.png b/images/part-10/staticinner.png new file mode 100644 index 0000000000000000000000000000000000000000..771697d93669da570b103f6fa6bd4531de011f51 Binary files /dev/null and b/images/part-10/staticinner.png differ diff --git a/images/part-3/class_hierarchy.PNG b/images/part-3/class_hierarchy.PNG new file mode 100644 index 0000000000000000000000000000000000000000..6b79506b8a66b067ac692f5994068d5b2793eb5b Binary files /dev/null and b/images/part-3/class_hierarchy.PNG differ diff --git a/images/part-3/class_hierarchy_2.PNG b/images/part-3/class_hierarchy_2.PNG new file mode 100644 index 0000000000000000000000000000000000000000..dccfad293085debf9c04593911badfe0272e5931 Binary files /dev/null and b/images/part-3/class_hierarchy_2.PNG differ diff --git a/images/part-3/class_hierarchy_3.PNG b/images/part-3/class_hierarchy_3.PNG new file mode 100644 index 0000000000000000000000000000000000000000..d32818b74a68151bb77b42935663e38dffe61d1a Binary files /dev/null and b/images/part-3/class_hierarchy_3.PNG differ diff --git a/images/part-3/class_hierarchy_4.PNG b/images/part-3/class_hierarchy_4.PNG new file mode 100644 index 0000000000000000000000000000000000000000..af408dc1e79e14b4ea05b4ff16a096bce2691ad4 Binary files /dev/null and b/images/part-3/class_hierarchy_4.PNG differ diff --git a/images/part-3/class_hierarchy_5.PNG b/images/part-3/class_hierarchy_5.PNG new file mode 100644 index 0000000000000000000000000000000000000000..0309ef8a77f5b9cbd10aa2830e84f506929d3e75 Binary files /dev/null and b/images/part-3/class_hierarchy_5.PNG differ diff --git a/images/part-3/inheritance-reuse.png b/images/part-3/inheritance-reuse.png new file mode 100644 index 0000000000000000000000000000000000000000..d2c7c4690597439317b8273825140a1b2fa70263 Binary files /dev/null and b/images/part-3/inheritance-reuse.png differ diff --git a/images/part-3/inheritance_example.png b/images/part-3/inheritance_example.png new file mode 100644 index 0000000000000000000000000000000000000000..e484527a0881dc255e52f467c0f8d0c55c2fce8c Binary files /dev/null and b/images/part-3/inheritance_example.png differ diff --git a/images/part-3/person.png b/images/part-3/person.png new file mode 100644 index 0000000000000000000000000000000000000000..e3431e2ae8730ed2bcdd28cd04dd7f311db5fce0 Binary files /dev/null and b/images/part-3/person.png differ diff --git a/images/part-9/casting.png b/images/part-9/casting.png new file mode 100644 index 0000000000000000000000000000000000000000..1ad014591725c243fb38c7bc9b43cd9d46cd191b Binary files /dev/null and b/images/part-9/casting.png differ diff --git a/images/part-9/dynamic-types.png b/images/part-9/dynamic-types.png new file mode 100644 index 0000000000000000000000000000000000000000..9048c5662b7b46a74bb24b46eb766e01ce96f174 Binary files /dev/null and b/images/part-9/dynamic-types.png differ diff --git a/images/part-9/unchecked.png b/images/part-9/unchecked.png new file mode 100644 index 0000000000000000000000000000000000000000..d6e8b8453a098a00989c995683727bae40013259 Binary files /dev/null and b/images/part-9/unchecked.png differ