Chapter III Based on the advanced characteristics of the class (Advanced Class-Based Features)
A main feature of traditional-oriented object-oriented languages is inheritance, subclassing, and subtyping. A lot of object-oriented language syntax, concept, from these three. For example, through SubClassing, you can inherit some methods of the parent class, while you can rewrite the parent class in the subclass. This rewritten method, through Subtyping, Subsumption, but also from a type of object to the object of the parent class.
However, inheritance, subclassing, Subtyping These three are not always in harmony. In some cases, the entanglement between these three will prevent the code reused by inheritance or generic. Therefore, people have begun to note the possibility of separating these three. Subclassing and Subtyping are very common. And other methods are still in the stage of research. This chapter we will introduce such a method.
First, object type
In early object-oriented languages (such as SIMULA), the type of type is mixed with the implementation of the method. This approach violates the principles that we have been widely recognized today have been widely recognized. This separation principle is particularly important when developing is a team.
More recent languages, distinguish between implementation and specifications by introducing object types that do not depend on implementation. Module-3 and other languages such as Java, such as Java, etc. are techniques that use Class and Interface.
In this book, when we start introducing instancetypeof (cell), its representative is quite limited. It seems that it seems to only represent the type of object generated by New Cell, so we don't use it to represent objects from other class New. But later, after we introduced Subclassing, Method Overriding, Subsumption and Dynamic Dispatch, things became less simple. Our InstanceTypeOf (Cell) can be used to represent objects from Cell's subclass New, which may include attributes and methods that are not Cell class definitions.
In this way, letting INSTANCETYPEOF (Cell) depend on a specific similarity. In fact, an object of an InstanceTypeOf (Cell) type does not necessarily talk to the Class Cell.
The only common in it and the Cell class is just the signature of all Cell class definitions.
Based on this consideration, we can introduce the syntax of the object type:
Definition for Cell classes and Recell classes:
Class Cell IS
VAR Contents: = 0;
Method get (): Integer IS
Return Self.Contents;
END;
Method Set (N: Integer) IS
Self.Contents: = N;
END;
END;
Subclass Recell of Cell IS
VAR backup: integer: = 0;
Override Set (N: Integer) IS
Self.backup: = self.contents;
Super.set (n);
END;
Method restore () IS
Self.Contents: = Self.Backup;
END;
END;
We can give such an object type definition:
ObjectType Cell IS
VAR Contents: integer;
Method get (): integer;
Method set (n: integer);
ObjectType Recell IS
VAR Contents: integer;
Var backup: integer;
Method get (): integer
Method set (N: integer);
Method restore ();
END;
These two types of definitions include all the types of properties and methods defined by all Cell classes and Recell classes, but do not include implementation. Thus, they can be treated as an interface that is not related to the detail to achieve specifications and implementation. Two completely unrelated classes C and C 'can have the same type of Cell, and the Cell type user does not have to care about the Class C or C' class.
Note that we can also add additional similar inheritance syntax to avoid rewriting the method in recording CELL in Recell. But that is just a balance.
Second, separation of subclassing and subtyping.
In the discussion of our last chapter, SubtyPe's relationship is based on the Subclass relationship. But if we want Type independent of Class, then we also need to define SubType independent of Subclass.
When defining subtype, we face several options: Subtype is determined by the type structure structure? Still determined by the name?
Subtype determined by the constituent structure of the type: If the type has all the properties and methods of type 2, we say that the type one is the type 2 Subtype.
Subtype determined by the type name is this: only when the type has all the required properties and methods, and the type one is explicitly declared as the type II Subtype, we recognize this relationship.
And if our choice is one, then those attributes and methods are necessary for Subtype? What is it possible?
Subtype determined by the constituent structure can perform type matching under the distributed environment and Object Persistence system (the translator's note: I am not very clear. It seems that the paper is still not enough). The disadvantage is that if the two types happen to have the same structure, but in fact, the wind is not blind, then it will cause errors. However, this error is to avoid it in some techniques.
In contrast, the name-based Subtype is not easy to accurately define, and the structure-based Subtype is not supported.
(Translator pressed here, I can't find the same feeling with the author. The shortcoming of Structure-based Subtype is at a glance, but the perfect way to avoid it, but I can't see it. Why can't I accurately define? C / java / c #, all popular OO languages only support the name-based Subtype, nor does it find out any flexible place. If you need a bridge between different names but similar structures, Adapter is completely competent! )
Currently, we can first define a simple structure-based SubtypE relationship:
For two types O and O ',
O '<: o When o' has all O type members. O 'can have more than O.
For example: Recell <: Cell.
For the sake of brevity, this definition does not take into account the normalization of the method.
In addition, when the type definitions are recursive (similar to the definition of the linked list), the definition of Subtype needs additional care. We will explain it in detail when you speak in Chapter 9 and after the chapter. (Translator pressed: Chapter 9. Rao! I want to die, I am tired?)
Because we don't care about the order of members, this subtype definition automatically supports multiple Subtype.
For example:
ObjectType Reinteger Isvar Contents: Integer;
Var backup: integer;
Method restore ();
END;
So, we have the following subtype relationship:
Recell <: Cell
Recell <: reinteger
(Translator pressed, the author's example did not take into account the details of the Interface could not contain the data domain. In fact, if we support Override on the data domain, not support shadowing - the author's structure-based Subtype semantics indeed implicit With this logic - then containing the interface containing the data domain, it is irrelevant, because the problem of a headache is no longer existed)
From this definition, we can draw:
If c 'is a subclass of C, then ObjectTypeOf (c') <: ObjectTypeOf (C)
Note that this definition is not established, that is, said:
Even if there is no subclass relationship between C 'and C, as long as the members they define meet the definition of our Subtype, ObjectTypeOf (c) is still established.
Go back and see Subclass-Is-subtyping in the previous chapter:
InstanceTypeOf (c ') <: instancetypeof (c) is only when C' is a subclass of C
In that definition, only when C 'is a subclass of C, ObjectTypeOf (c) can be set up.
In contrast, we have partially separated subclassing and subtyping. Subclassing is still subtyping, but Subtyping is no longer required to be subclassing. We call this nature "Subclassing-Implies-Subtyping" instead of "Subclass-Is-Subtyping".
Third, generic (Type Parameters)
In general, generics are technologies that reuse the same code in different types. It as a relatively independent of other object-oriented features, it has become more common in object-oriented languages. We discuss generics here, one is because of this technology itself is very interesting, in addition, because generics are the main tools that are used to deal with binary method proBlem.
Used in conjunction with subtyping, generics can be used to solve the difficulties of some types of systems that are caused by anti-co-change in methods. Consider an example:
We have two types of Person and Vegitarian, while we have two types of VEGITABLE and FOOD. Moreover, VEGITABLE <: Food.
ObjectType Person IS
...
Method Eat (Food: Food);
END;
ObjectType Vegetarian IS
...
Method Eat (Food: Vegitable);
END;
Here, from common sense, we know that a VEGITARIAN is a person. Therefore, we hope that there can be Vegetarian <: person.
Unfortunately, because the parameters are anti-co-change, if we mistake to think Vegetarian <: Person, according to Subtype's subsumption principles, a Vegetarian object can be used as Person. So a Vegetarian can be mistakenly eaten.
Using generic technology, we introduce Type Operator (that is, export another type from a type, conceptually similar to the type of function). Objectoperator Personeating [f <: food] IS
...
Method Eat (Food: F);
END;
Objectoperator vegetariaarance [f <: vegetable] IS
...
Method Eat (Food: F);
END;
The techniques used here are called Bounded Type Parameterization. (Trelli / Owl, Sather, Eiffel, Polytoil, Raptide, and Generic Java support bounded type parameterization. Other languages, such as C , only support simple types of generics)
F is a type parameter that can be instantiated into a specific type. Similar to the type definition of the variable, a Bound such as F <: vegitable limits F Only by VEGITABLE and its subtypes. Therefore, VEGITARIANEANEATING [VEGITABLE], VEGITARIANEATING [Carrot] are all legal types. And VEGITARIANEATING [BEEF] is not a legal type. Type VEGITARIANEANEATING [VEGITABLE] is an instance of VEGITARIANEATING, and it is equivalent to VEGITARIAN. (We use the structure-based subtype)
So we have:
For any F <: vegitable, vegitariangtang [f] <: personating [f]
For the original VEGITARIAN type, we have:
Vegetarian = vegetariangtang [vegetable] <: personating [vegitable]
This relationship is correctly expressed "a vegetarian is a person who eats vegetables".
In addition to Bounded Type Parameterization, there is a similar approach that can solve this vegetarian problem. This method is called: Bounded Abstract Type
Please see this definition:
ObjectType Person IS
TYPE F <: FOOD;
...
Var Lunch: F;
Method Eat (Food: F);
END;
ObjectType Vegetarian IS
TYPE F <: vegitable;
...
Var Lunch: F;
Method Eat (Food: F);
END;
Here, F <: Food means, given a Person, we know that he can eat some kind, but we don't know which one is specifically. This Lunch's properties provide this Person's food.
When you create a Person object, we can select a FOOD Subtype, such as F = Dessert. Then, use a variable of a Dessert type to assign a attribute LUNCH. Finally, an EAT (FOOD: Dessert) method is implemented.
In this way, Vegetarian <: Person is safe. When you treat a vegetarian as a Person handling, this VEGITARIAN can safely eat lunch, even if you don't know if he is eating, it is a meat.
This method is limited to, Person, Vegitarian can only eat lunch they own. You can't let them eat for lunch.