Abstract classes can have instance variables

19 polymorphism

Last update: April 30th, 2020
Treated commands: this, this (), instanceof, Casten, Object, override

learning goals

  • Interconnect constructors with this () and super ()
  • Recognize whether a static or dynamic type is present and work with instanceof and casting
  • Overwrite specific methods so that the correct method is called in the context
  • Use abstract classes and methods to make a class hierarchy more efficient and robust at the same time

We'll stick with the theory and look at how exactly constructors work and what it comes with Polymorphism (Diversity) is about.

We also see how to handle abstract classes and methods to make the code more robust against misuse.

19.1 dot notation

First of all, a short repetition of the Dot notation in the form of a video. Please make sure that you have mastered the dot notation.

Video: Dot Notation (7:03)

The dot notation for accessing variables and methods of objects is briefly shown here.

Summary

With dot notation, Processing / Java evaluates from left to right out. As soon as a point occurs, the method is executed and the return value is used. In the case of variables, the variable content is used. Then the next point is evaluated, if available.

19.2 this and this ()

Video: Constructors and "this" (9:44)

In this video I explain how a constructor is processed and how to use this () (with brackets!).

First of all, it is about the keyword. But be careful! - just like with keyPressed or mousePressed, there are two variants:

  • as a variable
  • as a function

Variable this

The variable is an object's pointer to itself. Why is that used? You are probably familiar with the following problem with writing a constructor:

public class Foo {// instance variables private String label; private int num; // Parameters and instance variables have the same name public Foo (String label, int num) {label = label; // Error! num = num; // Error! }}

You would like to name the parameters for the constructor the same as the instance variables. Why not? Why come up with a second name for each variable? The only problem is that Processing cannot distinguish which variable is meant where when it says "label = label". In this case, Processing must take the constructor parameter label for both sides, so that the value of the variable does not change (even if: it is the wrong one anyway, we want to set the instance variable after all).

The solution delivers. Because thanks to dot notation you can write and it is clear that the instance variable is meant here.

public class Foo {// instance variables private String label; private int num; // Parameters and instance variables have the same names as public Foo (String label, int num) { this.label = label; // Instance variable = parameter this.num = num; // instance variable = parameters}}

Function this ()

The function has a completely different meaning. The function is namely a constructor call of its own class, similar to how super () calls the constructor of the superclass.

Why do you want to call your own constructor? You do this when you have multiple constructors and want to entangle them to avoid code duplication.

Let's assume you want to offer a "base constructor" for the Person class. The age and height properties are automatically set to two basic values.

public class Person {private string first name; private string surname; private int age = 20; private float size = 1.80; public person (string first name, string last name) {this.first name = first name; this.lastname = lastname; } }

Now you are expanding the class with various "comfort constructors", where you can optionally also pass age and height:

public class Person {private string first name; private string surname; private int age = 20; private float size = 1.80; public person (string first name, string last name) {this.first name = first name; this.lastname = lastname; } public person (string first name, string last name, int age) {this.first name = first name; this.lastname = lastname; this.alter = age; } public person (string first name, string last name, float size) {this.first name = first name; this.lastname = lastname; this.size = size; } }

Notice you here Code duplication have available. In each of the constructors, you run the following code:

this.first name = first name; this.lastname = lastname;

Why could that be problematic? Perhaps if you process the passed names in some form before storing them in the instance variables.

In France, last names are often written in capital letters, so in a later version you will want to change the last name with toUpperCase directly in the constructor:

public class Person {private string first name; private string surname; private int age = 20; private float size = 1.80; public person (string first name, string last name) {this.first name = first name; this.lastname = surname.toUpperCase (); } public person (string first name, string last name, int age) {this.first name = first name; this.lastname = surname.toUpperCase (); this.alter = age; } public person (string first name, string last name, float size) {this.first name = first name; this.lastname = lastname; // Oops, forget! this.size = size; }}

As you can see, with Code duplication Made mistakes. Instead we use this () to call the base constructor from the other constructors:

public class Person {private string first name; private string surname; private int age = 20; private float size = 1.80; public person (string first name, string last name) {this.first name = first name; this.lastname = surname.toUpperCase (); } public person (string first name, string last name, int age) { this (first name, last name); this.alter = age; } public person (string first name, string last name, float size) { this (first name, last name); this.size = size; }}

You can see that this works in a similar way to super (), only that the constructor is searched for in its own class (not in the superclass). In our case, Processing looks for a constructor with two string parameters; this is obviously the first. The names are set there and the toUpperCase is also carried out, i.e. at a single point.

Summary

  • The variable points to the own object in which this call is located. If you are in the class and have an instance variable named, you can use to express that the instance variable of this class is meant. This is often used in the constructor to distinguish the instance variable from a parameter of the same name. You can call methods in the same way, for example:.
  • The function calls the constructor of its own class.
    • The parameter list of the call determines which constructor is chosen.
    • The this () call may only be made in a constructor and must be the first statement.
    • is used, for example, to write a general "base constructor" that other constructors use.

Exercises

19.2 a) (a) Salutation 1

Write a class with the customer single Instance variables "name" (string).

The class should three Constructors have: (a) with a name, (b) with first name and last name, (c) with first name, last name, title. The constructor should combine the names into a single string and store them in "name". Be sure to use this ().

Test your code with

public static void main (String [] args) {customer k1 = new customer ("Hans Meier"); Customer k2 = new customer ("Vera", "Schneider"); Customer k3 = new customer ("Dr.", "Heiko", "right-wing"); System.out.println (k1.name); System.out.println (k2.name); System.out.println (k3.name); }

You should see:

Hans Meier Vera Schneider Lawyer Dr. Heiko

19.2 b) (b) Salutation 2

The class stores letters in objects. The salutation ("Dear Mrs. Huber" etc.) is saved there as a string. The letter text is irrelevant here.

public class letter {private string salutation = "Dear Mr. Nobody,"; // Only placeholder text private String text = "Blah blah blah blah blah blah blah blah blah."; public String toString () {return salutation + "\ n \ n" + text; }}

Write constructors in which the salutation is "assembled", i.e. the instance variable is set again. (No new instance variables are required or desired.)

There should be three constructors so that you can get letters with

  • only the last name or
  • First and last name or
  • Can create first / last name and title.

Each constructor also receives a Boolean parameter that specifies the gender (true = female).

Think about how you can access a base constructor with the help of. Here you can see test cases that should then run:

public static void main (String [] args) {// test cases Letter b = new letter ("Schmidt", false); Letter b2 = new letter ("Vera", "Schmidt", true); Letter b3 = new letter ("Lea", "Kraft", "Prof. Dr.", true); // print out System.out.println (b); System.out.println ("---"); System.out.println (b2); System.out.println ("---"); System.out.println (b3); }

Note: You first write the base constructor, and the other two constructors each contain only one line with a this () call.

Your console should show:

Dear Mr. Schmidt blah blah blah blah blah blah blah blah. --- Dear Mrs. Vera Schmidt blah blah blah blah blah blah blah blah. --- Dear Ms. Prof. Dr. Lea Kraft bla bla bla bla bla bla bla bla bla bla.

19.3 Overwriting methods

Video: Override Methods (5:22)

In this video I explain how to "override" methods within a class hierarchy.

In a class hierarchy, subclasses may (and should) in certain cases replace a method of the superclass. Let us consider the following concrete example with the three classes, and. Class animal has the method speak ():

public class animal {public void speak () {System.out.println ("grrr"); }}

However, the subclasses should have their own variants of the method that make more sense for your class.

public class bird extends Tier {public void say () {System.out.println ("fieeeeep"); }} public class cat extends Tier {public void speak () {System.out.println ("meowuuuuu"); }}

We can test the methods in a static main method (e.g. within Tier):

public static void main (String [] args) {tier t = new tier (); Cat k = new cat (); Bird v = new bird (); t.spich (); k.speak (); v.speak (); } grrr meowuuuuu fieeeeep

You can see that - although methods are inherited - the "more specific" variant is always chosen. In the case of a cat, the method that is selected in the class animal is not the method that is defined in the class cat. That certainly corresponds to your intuition. They say the speak () method is used in the cat (and bird) class overwritten.

In English this is called method overriding and NOT overwriting - to override means "to override" or "to override something".

In order to be able to overwrite a method, you have to observe a few rules:

  • the method to be overwritten must have the same signature
  • the method to be overridden must have the same return type
  • the method to be overridden cannot be private or static

Summary

A subclass can be a method of the superclass with the same name, the same parameter list and the same return type redefine. This is called Overwrite. The more specific method is always executed at runtime.

19.4 Casting

Video: Static Type vs. Dynamic Type (8:12)

In this video I explain which method is called when in a class hierarchy.

In a class hierarchy, you have types and subtypes. Let's look at the class that is the upper class of the class:

public class Tier {} public class Vogel extends Tier {}

Then the following is correct:

Animal x = new bird (); // A bird IS AN animal

Conversely, you cannot save an object of the superclass in a subtype:

Bird v = new animal (); // An animal IS NOT always a bird

If we look again at the correct direction:

Animal x = new bird ();

But which one Type has x now? Is x of type or of type? After all, when we call a method, we need to know which class the method is being drawn from.

Let us consider the following concrete example with the three classes, and, which all have different variants of the method:

public class animal {public void speak () {System.out.println ("hum"); }} public class bird extends Tier {public void say () {System.out.println ("fieeeeep"); }} public class cat extends Tier {public void speak () {System.out.println ("meowuuuuu"); }}

Now what happens in the following code: Is the method being executed by or by?

public static void main (String [] args) {animal x = new cat (); x.speak (); }

If you run the code you will see:

meowuuuuu

Hopefully this corresponds to your intuition: The "more specific" method is always chosen. Why else should a programmer have implemented the method in? In the next section we will learn which rule this behavior is based on.

Static and dynamic type

The answer to the above question is: the static type is. So this is the type that you specify in the variable declaration. The dynamic type is on the other hand, that is the type that the object created in the example actually has.

Animal x = new bird (); // static: animal, dynamic: bird

The dynamic type is therefore always either the same as the static type or a subtype of the static type.

Why is this called static and dynamic? After you have defined a variable, this variable always has the same type, so the variable type remains static. On the other hand, the type of the contained object can also change, for example if you let the variable point to another object:

Animal x = new bird (); ... x = new cat (); // OK, since cat is a subclass of animal ... x = new animal (); // OK too, of course

Therefore, the type of the contained object is called the dynamic type, since it can change.

In the example above, the type of the contained object changes twice. The variable x still remains of the animal type. That is why the type with which the variable was declared (in the example: animal) is called thestatic type. The type of object that is currently in the variable is called the dynamic type.

Animal x = new bird (); // stat. Type = animal, dyn. Type = bird ... x = new cat (); // stat. Type = animal, dyn. Type = cat ... x = new animal (); // stat. Type = animal, dyn. Type = animal

Let's ask ourselves again which method is executed in the following code:

public static void main (String [] args) {animal x = new cat (); x.speak (); }

The variable has the static type and the dynamic type. Now we can formulate the rule according to which Java selects a method from the class hierarchy:

It will always the method of the dynamic type executed, if available.

If the method does not exist there, the method of the superclass that is closest to the dynamic type is selected.

Casting

Let's assume we have the following class hierarchy. Each class has the specified instance variables and corresponding getter methods:

You have a variable for Medium and save a song there:

Medium m = new Song ("OMG", "Marteria");

The static type of is in this case that dynamic type is.

If you want to call the getter method, you can't just do that because For Java, the static type is always decisive for access to variables and methods:

Medium m = new Song ("OMG", "Marteria"); String x = m.getArtist (); // Error!

In this case, the compiler (which translates your code into bytecode) refuses to translate the code because, from the compiler's point of view, it is not certain whether the variable contains a Medium object, a Song object or a Movie object.

For the Compiler the static type is always decisive, that is, the static type determines which variables and methods are available.

When you, as a programmer, are certain that the variable contains a song, you can use the compiler to forceto regard the variable as a song type. They say: you cast the variable to the subclass Song (from Engl. to cast) It will look like that:

Medium m = new Song ("OMG", "Marteria"); Song s = (Song) m; // m on type song cast string x = s.getArtist (); // now its okay

This is also called that Casting operator.

It can also be shorter:

Medium m = new Song ("OMG", "Marteria"); String x = ((Song) m) .getArtist (); // Casting and variable access

Unfortunately, the orgy of clips is necessary, otherwise the casting operator (song) will be the first to act to evaluated at the point, so too late.

instanceof

Sometimes you want to check what dynamic type a variable currently has. Imagine creating objects from a file with song / movie information and storing them in an array. Then you don't know where a song and where a movie is. With you can check whether an object is of a certain type.

Medium [] media = readMedia ("file.txt"); // here songs and movies are read in for (int i = 0; i media [i] instanceof song) {System.out.println ("Artist:" + ((Song) media [i]). GetArtist ()); }}

Thus, the operator tests whether an object (left side in the example media [i]) of a type (right side in the example, "Song") is. If media [i] were a subtype of Song, the operator would also return true.

Processing example, part 2

Hopefully you will remember our example with the animated balls and squares from Chap. 18.4:

Our goal is to move many objects with a list:

ArrayList things = new ArrayList (); for (mover m: things) {m.update (); m.render (); // Error: Mover has no render}

The problem is: The class has no method render (). Therefore, Java cannot call the render in the loop.

The solution is to introduce a "dummy method" in movers:

class Movers {PVector location; PVector speed; // Code omitted for clarity void render () {} }

Our class diagram now looks like this:

So we can now call the method in our loop, since Mover has a render ():

ArrayList things = new ArrayList (); for (mover m: things) {m.update (); m.render (); }

Since we know that every element is either of type Ball or of type Quad, one of the two special render methods is always called. The Mover method is never used!

But: Our solution is not yet optimal! A "dummy method" could seduce an inexperienced programmer into writing code there, but it is never executed. The second problem is the class Mover itself. The same programmer could get the idea of ​​creating an instance of Mover. but that does not make sense because Mover was created for purely "organizational" reasons, but because this is not to produce mover objects. Both problems are addressed in the next chapter.

Object: mother of all classes

Every class that you write yourself or that you find in front of you always has the class as the "top" superclass. If your class has no superclass, Objectautomatically defined as a superclass. The class Object is the root of every class hierarchy and thus the "mother of all classes".

Why do you need it? Since all objects in Java inherit the methods of the Object class, you can specify methods in Object that all objects require. One important example is the method your class automatically inherits, and you overwrite can to control the output with println (). Other interesting methods are and, but we don't want to go into that in depth here.

Another consequence is that a test is with every object.

Another benefit is that Variables of the type Object can store any objectbecause all types are automatically subtypes of Object.

// Examples with fictitious classes: Object a = new Person (); Object b = new airplane (); Object c = new Foo ();

Summary

  • If you create a variable of type A, the variable is always from static type A.
  • A type A variable can also contain type B objects, provided that B is a subtype (subclass) of A. The concrete type of an object at runtime (here B) is the dynamic type.
  • One may only use methods and variables of the static type.
  • With instanceof you can check the dynamic type. More precisely, is true if and only if is of type or any subtype of.
  • With Casting you can force the compiler to treat a variable as belonging to a subtype so that you can use the methods and variables of the corresponding (more specific) subtype. The spelling is when you want to cast on

Since one and the same variable can store objects of different types, one speaks of Polymorphism, in German "diversity".

Polymorphism (Diversity) in this context means that the call can take on different forms, depending on the (dynamic) type.

Exercises

19.4 a) (a) Class hierarchy and casting

Take a look at the following code and draw the class hierarchy as a diagram.

public class artist {} public class visual artist extends artist {} public class painter extends visual artist {public void male () {System.out.println ("times times times"); }} public class Sculptor extends BildenderKuenstler {public void haue () {System.out.println ("kracks"); }} public class musician extends artist {public void make music () {System.out.println ("tralala"); }}

Use the following main program and call the appropriate method (paint, hack, make music) on each variable by using casting:

public static void main (String [] args) {artist k1 = new painter (); Artist k2 = new sculptor (); Artist k3 = new musician (); // your code ...}

19.4 b) (b) instanceof

Create the following classes (you can also create an additional class for better structuring if you want).

  • mobile: Has property price (float)
  • IPhone: Subclass of cellphone
  • AndroidPhone: Subclass of mobile phone, has property manufacturer (string)
  • WindowsPhone: Subclass of mobile phone, has property manufacturer (string)

Write one constructor for each class, using super (), and write the toString () method.

In the main program you create an ArrayList of cell phones and add two sample objects from each class. Then use a foreach loop to write each object to the console with println (). Here, however, you should write "SOLD OUT" after the output for all iPhones and WindowsPhones. Take advantage of it.

19.4 c) (c) Casting

Create the following classes:

  • Animal: Has property name (String)
  • Fox: Subclass of Animal, has method hunt (), which only outputs "(name) is hunting" on the console (replace name with variable content)
  • Rabbit: Subclass of Animal, is method to flee () that only "(name) is fleeing" outputs on the console (name replaced with variable content)

Make a list with four sample objects (2 foxes, 2 rabbits). Foreach loop through the list and call either hunt () or flee (), depending on which class you are looking at.

19.4 d) (e) Travel bug

You are writing a program for a travel portal to manage users' trips.

Also pay attention to the use of packages in the following.

In the de.reisefieber.data package, write the class with the target (string) and costTransport (double) properties. Add a getter method for KostenTransport (pay attention to the correct naming of the method).

In the same package, write the subclass (subclass of travel) with the properties hotel (String) and costHotel (double). Also add a getter method for cost hotel here.

In the de.reisefieber.app package, write the class. This contains a list of trips and the following methods (should be self-explanatory):

  • new trip (string destination, double cost)
  • new trip with hotel (string destination, double cost, string hotel, double cost hotel)
  • calculateCostsTotal ()
  • calculateCostsHotels ()

Test your program with:

Customer account account = new customer account (); account.neueReise ("München", 50); account.neueReise ("Berlin", 150); account.newtravel ("London", 350); account.neueReiseMitHotel ("Stuttgart", 80, "B&B", 60); account.neueReiseMitHotel ("Berlin", 150, "Motel One", 70); System.out.println ("Hotel costs:" + account.berechneKostenHotels ()); System.out.println ("Total costs:" + account.CalculateKostenGesamt ());

You should see:

Hotel costs: 130.0 Total costs: 910.0

19.5 Abstract classes

Video: Abstract Classes and Methods (6:29)

It explains when you need abstract classes and methods and how to use them.

Abstract classes and methods

In our MyTunes example, we introduced a new class so that we can represent both movies and songs in an array.

It is noticeable that the class is medium is never instantiated. That is a good thing, because a medium-type object would be too unspecific to be useful.

Abstract class

To prevent an unsuspecting programmer from creating a medium object, we can save the class as a abstract declare.

public abstract class Medium {...}

With the keyword we tell Java that this class must never be instantiated. An abstract class can, however, contain instance variables and methods that it inherits.

Abstract methods

In abstract classes we can require certain methods from all subclasseswithout us already writing down any code. We call such a method without code, actually just the "promise of a method" abstract method. This is also marked with the keyword.

In the example it would make sense to define an abstract method play (), which is then implemented in the classes Movie and Song (i.e. only gets code there).

public abstract class Medium {... // all subclasses must implement this method: abstract void play (); }

Note that you are using the abstract method no access modifier specify. Access is regulated in the specific subclasses.

The subclasses Movie and Song must implement play (), i.e. have code (unless these classes are also abstract):

public class Movie extends Medium {... public void play () {// concrete code to play the movie}} public class Song extends Medium {... public void play () {// concrete code to play the song}}

Nevertheless, one could call play () in the main program for a variable of the type Medium (medium is the static type here). Then one of the two play () methods would be called, depending on whether the dynamic type Movie or song is.

Let's assume you search your database and get back an object of type Medium (static type). Now you are allowed to call play (), since it is defined as an abstract method in Medium:

Medium m = searchMedia ("OMG"); m.play (); // OK, since play () is defined in Medium

As we learned above, while the play () is taken of the current type (dynamic type), that song, if it's a song or movie, if it is a movie.

Processing example, part 3

We currently have the following class structure:

There are two problems:

  1. One could create instances of, although this is not wanted.
  2. The method of the Mover class is empty and only exists for the purpose of "making" the methods of the subclasses "known".

To solve problem # 1, we can turn to one abstract class do. To do this, we just have to put the word in front of the class definition:

abstract class mover {...}

If you now try to create a new object of the type, you will get an error message.

void setup () {Mover m = new Mover (); // ERROR }

That is exactly what we wanted to achieve! Otherwise, an abstract class can have instance variables and methods that it inherits. Abstract classes are ideal for moving common functionality "up".

How do we solve problem # 2? On the one hand we want to signal to Java that all subclasses have the method, but on the other hand we don't want to include a "dummy method".

For this purpose there is abstract methods. An abstract method has no code, it just is a promise that all (non-abstract) subclasses will implement this method. The whole thing is written like this - within the abstract class:

abstract void render ();

Abstract methods can of course also define parameters and return types. Logically, they can only occur in abstract classes.

Here again the complete code of:

abstract class mover {PVector location; PVector speed; Mover () {location = new PVector (random (0, width), random (0, height)); speed = new PVector (random (-3, 3), random (-3, 3)); } void update () {location.add (speed); if (location.x> width || location.x <0) {speed.x = -speed.x; } if (location.y> height || location.y <0) {speed.y = -speed.y; }} abstract void render (); }

In the class diagram, for abstract classes, write "<>" over the class name. The class name is written in italics. In the case of abstract methods, the method name is written in italics.

Our code is now well structured and robust.

Summary

A abstract class is used to prevent instances of the class from being created. To do this, you put the keyword in front of the class definition.

Abstract classes can contain instance variables and methods and pass these on to the subclasses.

If a method of an abstract class should not contain any code, but should only serve as a promise that all non-abstract subclasses contain this method, then one can use a abstract method define. The abstract method is also marked with the keyword.

An abstract method does not contain any code. The header is terminated with a semicolon. Every (non-abstract) subclass must fulfill this method, otherwise an error message will appear.

Exercises

19.5 a) (a) Class hierarchy

We look again at the classes from Exercise 19.4 (a):

public class artist {} public class visual artist extends artist {} public class painter extends visual artist {public void male () {System.out.println ("times times times"); }} public class Sculptor extends BildenderKuenstler {public void haue () {System.out.println ("kracks"); }} public class musician extends artist {public void make music () {System.out.println ("tralala"); }}

Which of the classes can / should abstract be? Draw this correctly on your class diagram.

19.5 b) (b) Animated objects

We take a look at the latest state of our animated objects:

ArrayList things = new ArrayList (); void setup () {size (200, 200); things.add (new Ball ()); things.add (new Ball ()); things.add (new Quad ()); things.add (new Ball ()); things.add (new Quad ()); } void draw () {background (0); fill (255); noStroke (); for (mover m: things) {m.update (); m.render (); }} abstract class movers {PVector location; PVector speed; Mover () {location = new PVector (random (0, width), random (0, height)); speed = new PVector (random (-3, 3), random (-3, 3)); } abstract void render (); void update () {location.add (speed); if (location.x> width || location.x <0) {speed.x = -speed.x; } if (location.y> height || location.y <0) {speed.y = -speed.y; }}} class Ball extends Mover {void render () {ellipse (location.x, location.y, 20, 20); }} class Quad extends Mover {void render () {rectMode (CENTER); rect (location.x, location.y, 20, 20); }}

Now you should change the quad so that it rotates and otherwise does not move in space.

To do this, change the class hierarchy as follows:

  • Introduce a new superclass that now contains the location.
  • Introduce another class that contains two properties: angle and angularSpeed. The instance variable angularSpeed ​​is chosen randomly in the constructor (between -0.1 and +0.1) and determines how the angle of rotation is adjusted.
  • Draw up a meaningful class hierarchy and determine where abstract classes / methods make sense.
  • Implement the classes and in particular modify Quad so that the box rotates (think of transformations, maybe push / popMatrix will help too).