[软件构造] 9 Construction for Reuse
[软件构造] 9 Construction for Reuse
Polymorphism in Java
Polymorphism in Java - GeeksforGeeks
The word polymorphism means having many forms. In simple words, we can define polymorphism as the ability of a message to be displayed in more than one form.
In Java polymorphism is mainly divided into two types:
- Compile-time Polymorphism
- Runtime Polymorphism
Type 1: Compile-time polymorphism
It is also known as static polymorphism. This type of polymorphism is achieved by function overloading or operator overloading.
Note: But Java doesn’t support the Operator Overloading.
Type 2: Runtime polymorphism
It is also known as Dynamic Method Dispatch. It is a process in which a function call to the overridden method is resolved at Runtime. This type of polymorphism is achieved by Method Overriding.
Dynamic Method Dispatch or Runtime Polymorphism in Java
Method overriding is one of the ways in which Java supports Runtime Polymorphism. Dynamic method dispatch is the mechanism by which a call to an overridden method is resolved at run time, rather than compile time.
- When an overridden method is called through a superclass reference, Java determines which version(superclass/subclasses) of that method is to be executed based upon the type of the object being referred to at the time the call occurs. Thus, this determination is made at run time.
- At run-time, it depends on the type of the object being referred to (not the type of the reference variable) that determines which version of an overridden method will be executed
- A superclass reference variable can refer to a subclass object. This is also known as upcasting. Java uses this fact to resolve calls to overridden methods at run time.
the version of m1( ) executed is determined by the type of object being referred to at the time of the call.
In Java, we can override methods only, not the variables(data members), so runtime polymorphism cannot be achieved by data members. For example :
// Java program to illustrate the fact that
// runtime polymorphism cannot be achieved
// by data members
// class A
class A
{
int x = 10;
}
// class B
class B extends A
{
int x = 20;
}
// Driver class
public class Test
{
public static void main(String args[])
{
A a = new B(); // object of type B
// Data member of class A will be accessed
System.out.println(a.x);
}
}
Output:
10
Since variables are not overridden, so the statement “a.x” will always refer to data member of super class.
Advantages of Dynamic Method Dispatch
- Dynamic method dispatch allow Java to support overriding of methods which is central for run-time polymorphism.
- It allows a class to specify methods that will be common to all of its derivatives, while allowing subclasses to define the specific implementation of some or all of those methods.
- It also allow subclasses to add its specific methods subclasses to define the specific implementation of some.
- Static binding is done during compile-time while dynamic binding is done during run-time.
private
,final
andstatic
methods and variables uses static binding and bonded by compiler while overridden methods are bonded during runtime based upon type of runtime object
Java details: final
• A final field: prevents reassignment to the field a3er ini9aliza9on
• A final method: prevents overriding the method
• A final class: prevents extending the class – e.g., public final class CheckingAccountImpl { …
https://baeldung-cn.com/java-type-casting
Downcasting
Let's sum up:
- Downcasting is necessary to gain access to members specific to subclass.
- Downcasting is done using cast operator.
- To downcast an object safely, we need instanceof operator.
- If the real object doesn't match the type we downcast to, then ClassCastException will be thrown at runtime.
final in java
Initializing a final Variable
We must initialize a final variable, otherwise, the compiler will throw a compile-time error. A final variable can only be initialized once, either via an initializer or an assignment statement. There are three ways to initialize a final variable:
- You can initialize a final variable when it is declared. This approach is the most common. A final variable is called a blank final variable if it is not initialized while declaration. Below are the two ways to initialize a blank final variable.
- A blank final variable can be initialized inside an instance-initializer block or inside the constructor. If you have more than one constructor in your class then it must be initialized in all of them, otherwise, a compile-time error will be thrown.
- A blank final static variable can be initialized inside a static block.
// Java Program to demonstrate Different
// Ways of Initializing a final Variable
// Main class
class GFG {
// a final variable
// 壹direct initialize 壹
final int THRESHOLD = 5;
// a blank final variable
final int CAPACITY;
// another blank final variable
final int MINIMUM;
// a final static variable PI
// direct initialize
static final double PI = 3.141592653589793;
// a blank final static variable
static final double EULERCONSTANT;
// 贰instance initializer block for
// initializing CAPACITY贰
{
CAPACITY = 25;
}
// static initializer block for
// initializing EULERCONSTANT
static{
EULERCONSTANT = 2.3;
}
// 叁constructor for initializing MINIMUM
// Note that if there are more than one
// constructor, you must initialize MINIMUM
// in them also叁
public GFG()
{
MINIMUM = -1;
}
}
Covariant
To summarize: Integer
is implicitly compatible to Number
, therefore Integer[]
is implicitly covariant to Number[]
, and Number[]
is explicitly covariant to Integer[]
.
a class can have two or more methods differing only by return type. javac uses this fact to implement covariant return types.
协变:子类的返回值比父类更具体,看成post-condition要求更强,实质是LSP对规约的要求导致。
反协变:子类的参数类型比父类更不具体,看成pre-condition要求更弱,规约变强,遵循LSP原则。实际按overload处理。
Generics are type invariant,are not covariant.
– ArrayList
is a subtype of List
– List
is not a subtype of List
类型擦除:
类型参数的信息在编译完成后被丢弃,故类型信息在运行时is not available
Type erasure: Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes
, interfaces
, and methods
.
如下图:
Node<T>
变成了Node
,T
变成了Object
两个错误示例:
运行时,myNumber实际被初始化成了integer
类型的数组,只是用Number
对象作为引用的类型来引用,故myNumber[0]=3.14
相当于把double
赋给integer
,造成了run-time error
。
我看学校就是把stackoverflow上的这个回答摘了下来做ppt。
编译阶段结束后,类型参数的类型信息(下面的Integer
和Number
)被丢弃,
这里没怎么看懂,去搜一搜:
Let's say
List<Number> l = new ArrayList<Integer>();
was a valid declaration. Then, through polymorphism, you could give l to a function declared as
public void foo(List<Number> list) { l.add(new Double(42.0)); }
According to polymorphism,
foo(l)
should be a perfectly valid call (Double
is aNumber
, after all), but you'd be adding aDouble
to anArrayList<Integer>
, which understandable violates type safety.
为了防止可能出现的违反类型安全的情况,编译器选择将此类行为标记成error
。
找搬也不搬全,搞的我以为第二张图片的第一个 pintln
语句不会报错。差评!😅
But if you attempt to implement the same code with generic collections, you will not succeed:
static long sum(List<Number> numbers) { long summation = 0; for(Number number : numbers) { summation += number.longValue(); //你看这里用了.longValue } return summation; }
You would get compiler errors if you try to...
List<Integer> myInts = asList(1,2,3,4,5); List<Long> myLongs = asList(1L, 2L, 3L, 4L, 5L); List<Double> myDoubles = asList(1.0, 2.0, 3.0, 4.0, 5.0); System.out.println(sum(myInts)); //compiler error //让你传int进去然后调用.longValue?想得美。 System.out.println(sum(myLongs)); //compiler error System.out.println(sum(myDoubles)); //compiler error
The solution is to learn to use two powerful features of Java generics known as covariance and contravariance.
ArrayList<Integer>
is a subclass of List<? extends Number>
not a subclass of List<Number>
. This is because generics are invariant in that for any two distinct types Type1
and Type2
, List<Type1>
is not a subtype neither a supertype of List<Type2>
, therefore a bounded wildcard type is used, as in List<? extends Number>
, to deal with these sort of situations.
通配符在泛型里的应用:
Lower Bounded Wildcards: <? super A>:?是A的超类
Upper Bounded Wildcards: <? extends A>:?是A的子类
通配子类关系:
List<Object>
is a subtype of List<? super String>
:
以>定义父、子类关系,则Object > String, Object < ? super Object < ? super String, 故成立。
如果你的ADT需要比较大小,或者要放入Collections或Arrays进行 排序,可实现Comparator接口并override compare()函数
委托:一个对象叫另一个对象干活。
当子类只需复用父类中的一小部分方法时,可用委托替代继承。-避免大量无用的方法。--Composite Reuse Principle (CRP)
“委托” 发生在object层面,而“继承”发生在class层面.
核心问题:每个Employee对象的奖金计算方法都不同、且可能会频繁地改变,需在object层面而非class层面实现。
Manager
调用从 Employee
继承来的bc.computeBonus()
,调用时,委托接口实现computeBonus
。
使用接口定义系统必须对外 展示的不同侧面的行为
§ 接口之间通过extends实现 行为的扩展(接口组合)
§ 类implements 组合接口
§ 从而规避了复杂的继承关系
composition VS aggregation
In composition (Person, Heart, Hand), "sub objects" (Heart, Hand) will be destroyed as soon as Person is destroyed.
In aggregation (City, Tree, Car) "sub objects" (Tree, Car) will NOT be destroyed when City is destroyed.
The bottom line is, composition stresses on mutual existence, and in aggregation, this property is NOT required.
Types of delegation
§ Use (A use one or multiple B)
§ Association (A has one or multiple B)
§ Composition/aggregation (A owns one or multiple B)
§ 都支持1对多的delegation——
class Professor {
List<Student> ls; //永久保存delegation关系
void enroll(Student s) {
this.ls.add(s); //建立delegation关系
}
void evaluate() {
double score = 0;
for(Student s : ls)
score += s.evaluate(this); //逐个delegate
}
}
White and Black Box Framework
Whitebox frameworks
– Extension via subclassing and overriding methods
– Common design pattern(s): Template Method
– Subclass has main method but gives control to framework
Blackbox frameworks
– Extension via implementing a plugin interface
– Common design pattern(s): Strategy, Observer
– Plugin-loading mechanism loads plugins and gives control to the framework