[软件构造] 9 Construction for Reuse

[软件构造] 9 Construction for Reuse

Polymorphism in Java

Polymorphism in Java - GeeksforGeeks

Runtime polymorphism

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

  1. Dynamic method dispatch allow Java to support overriding of methods which is central for run-time polymorphism.
  2. 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.
  3. It also allow subclasses to add its specific methods subclasses to define the specific implementation of some.

Static vs Dynamic binding

  • Static binding is done during compile-time while dynamic binding is done during run-time.
  • private, final and static 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 { …

image-20220610144034534

image-20220610144013230

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:

  1. 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.
  2. 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.
  3. 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>变成了NodeT变成了Object

image-20220610200635521

两个错误示例:
运行时,myNumber实际被初始化成了integer类型的数组,只是用Number对象作为引用的类型来引用,故myNumber[0]=3.14相当于把double赋给integer,造成了run-time error

image-20220610201009620

我看学校就是把stackoverflow上的这个回答摘了下来做ppt。

编译阶段结束后,类型参数的类型信息(下面的IntegerNumber)被丢弃,
这里没怎么看懂,去搜一搜:

https://stackoverflow.com/questions/4469667/problem-in-understanding-generic-collections-in-java/11073399#11073399

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 a Number, after all), but you'd be adding a Double to an ArrayList<Integer>, which understandable violates type safety.

为了防止可能出现的违反类型安全的情况,编译器选择将此类行为标记成error

image-20220610201020992

找搬也不搬全,搞的我以为第二张图片的第一个 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的子类

通配子类关系:
image-20220610220658361

List<Object> is a subtype of List<? super String>
以>定义父、子类关系,则Object > String, Object < ? super Object < ? super String, 故成立。

如果你的ADT需要比较大小,或者要放入Collections或Arrays进行 排序,可实现Comparator接口并override compare()函数

委托:一个对象叫另一个对象干活。
image-20220610223751779

当子类只需复用父类中的一小部分方法时,可用委托替代继承。-避免大量无用的方法。--Composite Reuse Principle (CRP)

“委托” 发生在object层面,而“继承”发生在class层面.

核心问题:每个Employee对象的奖金计算方法都不同、且可能会频繁地改变,需在object层面而非class层面实现。

image-20220610225309082

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

posted @ 2022-06-11 10:58  Matrix_250  阅读(33)  评论(0编辑  收藏  举报