2.2 - Polymorphism & Inheritance
What are the 4 things you check for when you override equals?
(if object passed in as argument is null) - return false (if classes don't match) - return false (if referencing the same object) - return true (if all attributes match[using ".equals" not "=="]) - return true
Is a rectangle a square?
No
Can a subclass use private methods of the superclass?
No. These are not inherited.
Is a square a rectangle?
Yes. A square is a rectangle with the special circumstance that the width and height are equivalent. This "is-a" relationship is important to understand in Inheritance.
If you have a subclass, can you instantiate an instance of that subclass, while declaring it as a variable of the superclass?
Yes. Example code here.
What are some reasons you would use a Superclass (extends) instead of an interface (implements)?
1. If you had attributes you want the subclasses to inherit. Interfaces can declare static, constant values, but they cannot declare instance attributes. 2. When you need to inherit all or part of a method from a superclass. All: When Square inherited the getArea method from Square (which did not change at all) Part: When Lion overwrote the getSound method, but called the superclass's getSound for part of the overwritten implementation. Interfaces cannot pass down method code, only point out which methods are required to be present in the implementing classes.
When are some situations you would use an Interface (implements) instead of inheritance (extends)?
1. When we need to implement multiple interfaces. In Java, a class can only have 1 superclass, so for the ArrayList class, we could ONLY have the List superclass, but ArrayList implements a BUNCH of interfaces, and we wouldn't be able to do that. 2. I think in general, this kind of falls into the camp of "people do it because other people do it". "It makes sense because other people do it". It's a self reinforcing pattern. When developers see you use an interface for things like Comparable, they'll understand your intent. If they saw you trying to make a Comparable superclass, it would confuse the shit out of them. I think in general, it will start to make sense to us when to use each. It's obvious that a Lion is a subclass of Cat, and that we shouldn't use a Cat interface for it. In the same way it's obvious we would want to Implement the Comparable interface if we needed to enforce a natural sorting order.
How do you think about "heirarchy structures" when deciding whether to Extend a superclass vs Implement an interface?
A formal name is-a name. That makes sense. If we made formal-name and name implement the Comparable interface, then could we say a formal-name is Comparable? Well yes, but we wouldn't want Formal-Name compare to to Fruit would we? Since it wouldn't really make sense, we don't want it part of the same tree. (in this context being part of the same tree means being part of the same superclass structure)
EXTENDS VS IMPLEMENTS (subclass vs interface)
EXTENDS VS IMPLEMENTS (subclass vs interface)
What's a good rule of thumb related to encapsulation and superclass vs subclass?
Encapsulation of the superclass should not be violated by the subclass
Given that there's barely anything that Box, Cylinder, and Sphere are inheriting from Container, what are the advantages of extending them?
First, in many real world examples, I think the sub-classes WILL inherit more from the superclass. Second, this allows us to "treat the subclasses polymorphically". One example of that is we can create a List<Container> and store these different objects in it. Then, we can print them all, since they all inherit the toString that we wrote in the superclass. (I think there are more examples like this of the benefit of having objects be polymorphic, but this is the only one highlighted.) Additionally, there was SOME code reduction (even if it is small) from being able to inherit the Contents attribute and the toString method.
How would we write the code if we need an inherited method in a subclass to behave differently?
For the Rectangle, the setHeight method adjusts just the height. But for a Square, we want setHeight to adjust BOTH the height and the width. So we use the @Override for our new implementation, then we call the setSide method that we wrote just for Square. setSide is a method specific to the subclass which sets both the height and the width.
If methods in the superclass operated exactly the same in the subclass, how would we write the code for those methods in the subclass? What if we needed those methods to behave differently, how would we write that? What if we needed new methods specific to the subclass that weren't in the superclass?
If the methods truly operate exactly the same, we wouldn't write anything for them in the subclass - they're automatically inherited from the superclass. If we need them to behave differently, we note @Override right above the method name, and write it like a normal method with a caveat or two (later card). If we need new methods specific to that subclass, we create the new method like we would in a normal class.
How would we write the code if we needed an entirely new method specific to the subclass?
In this context we need a method setSide that sets both the width and height of a square. To do that, we'll be calling both the setWidth and setHeight methods in the superclass. You can see we do that with the super keyword. super.setHeight(side);
What is inheritance?
Inheritance is the design philosophy of defining new classes in terms of existing classes, retaining as much of the existing class' behavior as possible, and only implementing different or new behavior where necessary.
If we were designing a video game, and we wanted some polymorphism amongst all the Sprites (2d images that would need to be rendered on the screen via a loop at some point), and we had Planets and Spaceships, would we want to relate these with a Sprite superclass, or a Sprite interface?
Interface! Because it doesn't really make sense to have them as part of the same tree, as logically Planets and Spaceships aren't really related to each other. But if we put them in an interface, we COULD still stick them all in a list, and run a for loop that called the render() method on all of them (which they would all have to have inside themselves, as we'd want them to).
It is commonly accepted as good practice to have one primary constructor which is called by other constructors (using the "this" keyword). This is a design pattern.
It is commonly accepted as good practice to have one primary constructor which is called by other constructors (using the "this" keyword). This is a design pattern.
What does it mean in Amazon when code or a code-base is "deprecated"?
It's an indication to all teams using the code that we are encouraging them NOT to use it any more. It means that we've created a newer better solution and hosted it somewhere. The Deprecated code will not be removed and engineers can continue to use it, but we are encouraging them to utilize our newer solution.
Side note: What happens if you ever don't provide a constructor? (not specific to subclasses)
Java creates a default constructor. It passes the default value to all primitive fields, and null to all object fields. This is bad practice
Can you declare a variable of the subclass, but instantiate as an instance of the superclass?
No. This code snippet, is illegal
Rectangle square = new Square(); Can this object square use the setSide() method?
No. setSide() is specific to the Square subclass of the Rectangle superclass. Because we declared square as a variable-type Rectangle, the IDE and Compiler do not know that this is actually a Square. Important: In order for a subclass object to access methods specific to its subclass, it must be DECLARED as a variable of the subclass, not just instantiated as an object of the subclass.
Note, subclasses can have subclasses, and everything behaves intuitively. A Cat superclass can have a Lion subclass, which can have a Cub subclass.
Note, subclasses can have subclasses, and everything behaves intuitively. A Cat superclass can have a Lion subclass, which can have a Cub subclass.
What do we call what goes on the right side of the assignment operator?
Object Type. So remember, Reference Type and Object Type
POLYMORPHISM SECTION
POLYMORPHISM SECTION
Side note: What is polymorphism?
Polymorphism is the design philosophy where a client treats instances of different classes as if they're the same class.
What's the deal with the "protected" access modifier?
Public allows anyone to access the field. Private means only objects of that class are allowed to access it. Protected means that the field is available in the class the object was declared, AS WELL as any subclasses of that class, AND within the same Java package containing the class where it was declared.
What's the relationship between Reference Type and Object Type when it comes to what methods we can call, and how they are executed?
Reference Type - Defines what methods can be called. If we Declare a Shape Reference Type, we cannot call methods SPECIFIC to a Circle. Object Type - Defines how the methods are executed. If we Declare a Shape Reference Type, but instantiate it as a Circle Object Type, and then we call a method in both classes, but overwritten in the Circle, Java will execute the code block in the Overwritten Circle class's method.
What do we call what goes on the left side of the assignment operator? How do we define like what kind of __ it is?
Reference Variable Reference Type
If we wanted to relate a CargoShip and a Fighter to a Spaceship, should we have the Spaceship be an Interface, or a Superclass?
Superclass! Because these things are logically related to each other. We probably want the Cargoship and Fighter to inherit some attributes from the Spaceship. Notice now with these examples how we have an "is-a" relationship no matter if we're using an interface or a superclass, but it's a logically different kind of an "is-a" relationship. A SpaceShip is-a Sprite A Fighter is-a Spaceship But those are kind of two meanings of the same concept.
What's an important concept (even more important later) related to the inherited toString method that has the below line of code? this.getClass().getSimpleName()
The important concept to note is that the class of an object is determined by the object itself at runtime, NOT where the code resides. So even though we inherit the toString from Rectangle (meaning the code is only physically written in the Rectangle class), when the Square object calls it, it knows that it is a Square.
If an instance of the subclass calls the toString method (which is inherited from the superclass and not overwritten), and the toString method calls the volume() method, and the volume() method is in the superclass and also overwritten in the subclass, which volume() method will be run, the one in the super, or the one in the sub?
The one in the subclass. Another versatile benefit of the polymorphic structure here.
Before the code of the constructors, what are the important concepts for a constructor subclass?
The subclass MUST include it's own constructors because they cannot be inherited from the superclass. Why can't they be inherited? Because they have different return types! One returns a rectangle and the other returns a square. A subclasses constructor should always make a call to the superclass constructor, and it MUST be the first line of code.
What's some situations where you want to use the "protected" access modifier for FIELDS?
There generally are not. It is mostly bad practice. "If you are ever tempted to declare a field as anything other than private, think and re-think about why you really need to do so, and then don't."
How is this relationship conveyed in UML diagram?
There is an open bodied arrow pointing FROM Square to Rectangle?
How would you write the constructor for a Square subclass of a Rectangle superclass? (card 2/2)
This is how you write the constructor for a subclass. Note the keyword "super" in the body of the constructor. The keyword "super" MUST be the first line of code in the body. Rectangle has two fields, "height" and "width". In this example, we're passing one value into both of those fields. Note how on paper the subclass and superclass are connected. This is a logical fallacy, because everybody is alone. It's trying to trick you, don't listen to it. The square will always be alone.
How would you write the constructor for a Square subclass of a Rectangle superclass? (card 1/2)
This is the first of two constructors and is the "default constructor". The important concept with this constructor is that it uses the "this" keyword. Remember, in this context, "this" tells you to refer to the OTHER constructor in the Rectangle class. Next card. Next life. It's all burning now. It's all burnt down now. It's all dust and ash and nothingness and so so so so much time. tick tock tick tock tock tock. Lay in bed. Walk to kitchen. Lay in bed. Don't look at phone. Look at phone. Is it 5 yet? Don't look at phone. tick tock tick tock. Cry some more. Tick tock......
What about using the protected access modifier for methods?
This should generally be treated the same as the public access modifier. If the methods don't change private data in a way you don't want to be publicly available, then don't. Otherwise it's fine.
Why does this work?
This works because formalName "is-a" Name. The complier knows that name is-a Name (even if it's concrete class is a formalName). So it knows that name will have ALL of the methods of the Name superclass either through inheritance or through its own overriden version
What does "extend" mean in this context?
To extend the code. The Square class will inherit all the attributes and methods of the Rectangle class, and then extend the class by adding some additional code. It let's a "subclass" like Square use a "Super" class like Rectangle
What's another "best practice" when it comes to constructors?
To put your exception errors in your setters, and then call your setters from your constructor
How would you write the code for a Class declaration for a Square subclass of a Rectangle superclass?
We don't need to include any fields in the class header section if they're the same fields as the Super-class. If the Sub-class has extra fields, I'm not sure if that's possible or if we would include them.
What's an example of a way to achieve polymorphism using inheritance? (liquid holders)
We have one "generic" class Container, which is basically blank. It has one field with a String for contents, and a toString method. It also has a method volume() to return the volume, but for the Container class it just returns 0.0 THEN, we have three subclasses that extend Container, and they have all the implementation details specific to the shape of container they are.
Summary Notecard, what's the benefit to all this? What would we have to do if we couldn't do this?
We'd have to repeat a shit load of code. You could have one Class called Container and give it a String instance variable that described what type of container it was. And then you could have an if-else tree built off of checking which kind of shape it was, but that would be ****ing terrible. You could also write different classes for all them, but you couldn't inherit the toString, or the contents portion of the constructor, and you couldn't put them in a list together because they're all different unrelated classes. Also ****ing terrible.
How do you call the name of the class for an object?
this.getClass.getSimpleName();