In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-04-10 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
This article mainly introduces "what are the Scala type systems and functions". In the daily operation, I believe that many people have doubts about the Scala type system and functions. The editor consulted all kinds of materials and sorted out simple and easy-to-use operation methods. I hope it will be helpful to answer the doubts about "what are the Scala type systems and functions?" Next, please follow the editor to study!
Summary
Until last time, Mr. Haneda introduced the features of Scala grammar. As a guest, I would like to introduce the type system and related functions of Scala. The focus of this introduction is the class level difference between Java and Scala, paradigm covariant and inverter, real type (Existential Type), structure type (Structural Type) and compound type (Compound Type).
Similarities with Java
The basic part of the Scala type system is very similar to Java. Like Java, Scala has a single root class, Java implements multiple inheritance through interfaces, while Scala implements multiple inheritance through trait (Scala features can contain implementation code, which is certainly different from Java interfaces. However, because the feature itself has the function of type, it can be considered equivalent to the interface of Java for features that do not contain implementation code.
However, there are several points in which Scala has a different part from Java, or an additional functional part compared to Java. The following will focus on the differences or additions compared to Java to illustrate the type system of Scala.
Class hierarchy of Scala (1)-Any, AnyVal, AnyRef
As mentioned in the fourth part of this series, the type hierarchy of Scala is very similar to the corresponding part of Java. First, there is a single root class Any in Scala, and all types are inherited directly or indirectly from the Any class. This is the same as the root class of all reference types in Java is java.lang.Object.
On the other hand, there are different parts from Java. First of all, Scala does not have a Primitive type similar to that in Java. It is
What's going on? All the types in Scala that are equivalent to the base types become classes, and these classes are subclasses of the AnyVal class that inherits Any.
In addition, all reference types are indirect or direct subclasses of the AnyRef class. I said earlier that the Any class is similar to the java.lang.Object class in Java, but in a practical sense, the cause corresponding to java.lang.Object should be AnyRef.
But digress, for Scala equivalent to the basic type of class, you can think of integers (Byte, Short, Int, Long) and floating-point numbers (Float, Double) as adjacent siblings, then Java can be converted by default, such as byte- > int, whether you need to show the type conversion in Scala, if necessary, then we will find it troublesome. In fact, there is an implicit conversion (implicit conversion) feature in Scala that enables users to define their own default type conversion capabilities. For default conversion functions such as byte- > int and float- > double in Java, an equivalent implicit conversion function is defined in the Scala standard library, so users do not need to display the conversion. This implicit conversion is a very interesting feature, which may be explained in more detail in a later series.
Class hierarchy of Scala (2)-Nothing, Null
Unlike Java, there is a so-called bottom type in Scala, which is the Nothing class. Nothing is a subclass of all types, which means that you can assign the Nothing type to a variable of any type, but the value of the Nothing type does not exist.
You might think, "what's the use of a type without a value?" . But the Nothing type definitely plays an important role in representing the return type of a function that does not return a value, or in representing an empty set in the paradigm described later.
There are also subclasses that are all reference types (AnyRef) that can be assigned to all types of Null that reference type variables. The value of the Null type is only null (in fact, there is also a Null type in Java, which is responsible for similar tasks to the Null type in Scala. Unlike Scala, the opportunity to define Null types is not shown in Java, so basically no one is aware of it. The relationship between the above types is indicated by the b of "3mur1".
Paradigm foundation
In a word, a paradigm is the ability to define a class or interface (characterized in Scala) that takes a type as a parameter. Java from the beginning of JDK5 has a paradigm, presumably know more people, the following is a simple example.
For example, suppose you have the following code snippet. Here java.util.List is the generic interface, and String is the type parameter assigned to it.
Java.util.List
< String>Strs = new java.util.ArrayList
< String>()
In this way, objects of type String (or subtype) can be added to the List in the following way.
Strs.add ("hoge")
As shown below, a compilation error occurs if an object other than String is added to List.
Strs.add (new java.util.Date ())
This allows you to develop type-safe universal set (collection) libraries. The collection libraries before Java5 were implemented in Object. However, there is no correct type checking when adding elements to the set, and mandatory type conversions are required when the elements are extracted from the set, resulting in some type safety problems in the old collection library. Furthermore, the type definition alone does not tell what kind of elements the set contains, so it is also insufficient in terms of readability.
The paradigm of Scala is very similar to that of Java and can basically be used in the same way, except that there are some differences in marking methods. Here is the Scala code that is basically the same as the Java code.
Var strs: java.util.List [String] = new java.util.ArrayList
Use [..] in Scala. To replace the Java
< ..>To represent the type parameter table. Incidentally, unlike Java, Scala does not need to specify String type parameters when new ArrayList, which is because the compiler's type inference works (it is also possible to display the specification).
The method of defining a paradigm class in Scala is basically the same as Java. The following is an immutable unidirectional list class defined by Java through the paradigm. Here, after the class name Link, you declare the use of
< >The enclosed type parameter T. This type parameter T can be used like a normal type in the definition of the Link class.
Class Link
< T>{final T head; final Link
< T>Tail; Link (T head, Link)
< T>Tail) {this.head = head; this.tail = tail;}}
You can also use Scala to define exactly the same list of paradigms as above.
Class Link [T] (val head: t, val tail: Link [T])
From this, in addition to a few subtle identity differences, Scala can also easily use paradigms.
Covariance and inversion of paradigm
Judging from the instructions so far, one might think that Scala has just changed the logo of the paradigm in Java. But there are several obvious differences between the paradigm in Scala and Java, one of which is the covariant and inverter mentioned here.
Covariation
The so-called covariation in the paradigm is something like this. First assume that there are classes G (or interfaces and features) and types T1 and T2. If T1 is a subclass of T2, if G
< T1>It is also G
< T2>Then class G is covariant.
If this is the only explanation, it is more difficult to understand, so give an example. As shown below, assume that there is a type of java.util.List
< Object>Variable S1 and type java.util.List
< String>The variable S2.
Java.util.List
< Object>S1 =...; java.util.List
< String>S2 =.
String is a subclass of Object, and assigning S2 to S1 is not allowed in Java, which will result in a compilation error. So, although String is a subclass of Object, java.util.List
< String>It's not java.util.List.
< Object>So classes or interfaces defined using the Java paradigm are not covariant This is not because the Java paradigm is not flexible, but because the covariant paradigm has some problems in ensuring the security of the type.
Assume that S1 / S2; is allowed. S1 holds elements of type Object, so you can add objects of type java.util.Date as shown below.
S1.add (new java.util.Date ())
However, because the statement S1 contains s2tramt S1 is pointed to S2, the List variable S2 that holds the String element can be added to the java.util.Date object. In this way, it is not easy to use paradigms to ensure type safety (java.util.List
< String>Only String) was destroyed. Because of this problem, the paradigm of Java is not covariant.
Incidentally, for arrays that existed before Java5, if the element type An of the array is a subclass of the element type B of the array, then the array type of An is also a subclass of the array type of B. that is to say, the array in Java is covariant. In this way, as shown below, even the assignment between arrays that violate type safety (without casting) code can pass the compiler check.
String [] S2 = new String [1]; Object [] S1 = S2; S1 [0] = new java.util.Date (); / / throw an ArrayStoreException exception during execution
As mentioned above, there is a reason why the paradigm in Java is not covariant, but in some cases the limitation is too strong. For example, take the use of the immutable link class mentioned above as an example. In this case, once an instance of immutable Link is created, unlike Java's List, write operations (such as add) cannot be performed for this instance, so Link
< String>Assign a value to Link
< Object>You can think of it as no problem, but this is not allowed in Java.
The paradigm of Scala, like Java in the absence of a specific specification, is non-covariant. For example, a compilation error will occur after writing the following code using the aforementioned Link class.
Val link: Link [Any] = new Link [String] ("FOO", null).
The error message is as follows. The reason for the error in the description is that there is a Ling [Any] where it should be, but there is a Link [String], which is the result that Link is not a covariant.
Fragment of Link.scala): 2: error: type mismatch; found: this.Link [String] required: this.Link [Any] val link: Link [Any] = new Link [String] ("FOO", null)
However, in the paradigm definition of a class or feature in Scala, if you add the + symbol before the type parameter, you can make the class or feature covariant. The following is an experiment to define a covariant class in Scala. The theme is the aforementioned Link class, with a + symbol before the type parameter T.
Class Link [+ T] (val head: t, val tail: Link [T])
Once the Link class is defined in this way, the code with the previous compilation error can be compiled smoothly. In addition, a compilation error will occur if you try to define a covariant paradigm that does not guarantee type safety. For example, this limitation can cause problems when defining immutable data structures. For example, for the previous Link class, append a method prepend that puts the element passed in as a parameter in the column header and returns a new list.
Class Link [+ T] (val head: t, val tail: Link [T]) {def prepend (newHead: t): Link [T] = new Link (newHead, this)}
The prepend method does not change the state of the original Link class instance, because there should be no problem. However, the following compilation errors occur after compilation.
Ink.scala:2: error: covariant type T occurs in contravariant position in type T of value newHead def prepend (newHead: t): Link [T] = new Link (newHead, this)
In fact, after the paradigm becomes covariant, the type parameters cannot be placed unmodified on the parameters of the member method (in this case, newHead). However, this problem can be avoided by defining the member method as a paradigm and describing it as shown below (the specific reason is not discussed here).
Class Link [+ T] (val head: t, val tail: Link [T]) {def prepend [U >: t] (newHead: U): Link [U] = new Link (newHead, this)}
Paradigm methods can also be defined in Java, just like paradigm type definitions, which define type-safe paradigm methods by parameterizing methods with type parameters. For example, the map method of the List class that serializes the fifth appearance is the paradigm method.
Verride final def map [B] (f: (a) = > B): List [B]
The map method applies the function f passed in as an argument to all elements of the List and returns after forming a list of the application results of the function. But what the return result of the parameter function f is in the definition of the map method is unknown, so the type parameter B is used to make the map a paradigm method, so that it can be used in various types.
The paradigm method is done by using [..] directly after the method name. To define it by enclosing the type parameter. Type parameters enclosed in [] can be used as general types in methods. And add >: or after the type parameter
< :符号后,可以将类型参数所表示的类型限制为某一类型子类或父类。例如,[U< :T]的情况下,U必须是T的子类;[U>In the case of: t], U must be the parent of T.
Inverse transformation
On the other hand, the inverter in the paradigm is something like this. First assume that there are classes G (or interfaces and features) and types T1 and T2. If T1 is a subclass of T2, if G
< T2>It is also G
< T1>(note that left and right are the opposite of covariance), then class G is inverted.
As with covariance, here is an example. First of all, suppose there is a type of java.util.List
< Object>Variable S1 of type java.util.List
< String>The variable S2.
Java.util.List
< Object>S1 =...; java.util.List
< String>S2 =.
String is a subclass of Object, and because the Java paradigm rule does not allow the expression s2=s1, a compilation error will occur. Here, although String is a subclass of Object, java.util.List
< Object>It's not java.util.List.
< String>So the paradigm of Java is not inverted. If the Java paradigm is inverted, then, as in the case of covariance, there will be type safety problems.
Suppose the expression s2=s1 is allowed. Because the element type of S2 is String, the type returned after fetching the element from the list should be String. Therefore, the following code should be valid.
String str = s2.get (0)
However, the element type of the list S1 referred to by S2 is Object, so the extracted elements in the S1 list are not limited to String, which is a problem with type safety.
For the paradigm of Scala, if there is no special instruction, it is not inverted like Java. Suppose you have the following LessTan class with the apply method (the logic of the apply method is to return true when an is less than b, or false if not).
Abstract class LessThan [T] {def apply (a: t, b: t): Boolean}
The following method that uses the LessThan class will have a compilation error.
Val hashCodeLt: LessThan [Any] = new LessThan [Any] {def apply (a: Any, b: Any): Boolean = a.hashCode
< b.hashCode } val strLT: LessThan[String] = hashCodeLt ... 编译错误的文本如下。显示的错误原因是在因该出现LessThan[String]的地方出现了LessThan[Any],由此看见LessThan类不是逆变的。 (fragment of Comparator.scala):5: error: type mismatch; found : this.LessThan[Any] required: this.LessThan[String] val strLT: LessThan[String] = hashCodeLt 但是,在类或特征的定义中,在类型参数之前加上一个-符号,就可定义逆变范型类和特征了。下面尝试一下定义Scala的逆变类。题材是前面的LessThan类,如下所示在LessThan定义的类型参数前加上-符号。 abstract class LessThan[-T] { def apply(a: T, b: T): Boolean } 将LessThan类如此定义之后,前面错误代码的编译就可以通过了。另外,如果将类型定义为逆变后会发生类型安全性问题,则编译器将报编译错误。 实存(Existantial)类型 前面说过了Java范型没有协变和逆变特性,但是通过使用Java的通配符功能后可以获得与协变与逆变相近的效果。通配符不是标记在类型定义的地方,而是在类型使用的地方,可以在使用类型处加上G< ? extends T1>Or G
< ? super T1>.
The former corresponds to covariance. When T2 is a subclass of T1, G
< T2>It's G.
< ? exnteds T1>A subclass of the. The latter corresponds to the inverter. When T1 is a subclass of T2, G
< T2>It's G.
< ? super T1>A subclass of the. Therefore, the following code will compile normally.
Java.util.List
< String>S1 =...; java.util.List
< ? extends Object>S2 = S1; / / corresponding covariant java.util.List
< Object>S3 =...; java.util.List
< ? super String>S4 = S3; / / corresponding inverter.
Because the wildcard is marked where the type is used, it is used every time you define a covariant or inverted variable. The disadvantage is that it is cumbersome. On the other hand, even if it is not defined as a covariant or inverted paradigm type, it is its advantage that it can be handled in a covariant or inverted way.
The same function as wildcards in Java can also be achieved in Scala by using the real type method class. For example, the following Scala code can achieve the same functionality as the above Java code.
/ / java.util.List [_
< : Any] (省略形式) var s1: java.util.List[String] = new java.util.ArrayList var s2: java.util.List[T] forSome { type T < : Any } = s1 //java.util.List[_ >: String] (omitted form) var S3: java.util.List [Any] = new java.util.ArrayList var S4: java.util.List [T] forSome {type T >: String} = S3.
Structure (Structural) type
In a language similar to Java, their inheritance relationship can be determined only after the class has been defined. Suppose you have the following three Java class definitions: a, B, and C.
Class A {void call () {}} class B extends A {void call () {}} class C {void call () {}}
If there is a method void foo (An a), then instances of types An and B can be passed to it as parameters, but type C cannot be passed (although method call is also defined in C).
This is because class B clearly identifies the inheritance relationship with A through the exnteds A statement, while C does not clearly indicate the inheritance relationship with A, so C is not a subclass of A. This is a matter of course for static languages like Java and C# (except in the case of C++), so not many people should realize it. On the other hand, in the dynamic language community, when this is called duck typing, the more common view is that "whether there is an inheritance relationship or not, as long as there is a required method in the object." . Take the code just now as an example. Call methods are defined in A, B, and C. if only the call method is called in the foo method, then an instance of class C can also be passed to foo as a parameter.
It is often heard that "it is precisely because there is no static type checking in dynamic languages that duck typing functionality can be used." . But if you think about it a little bit, although it's a static language, you still know which methods the type holds at compile time. In theory, even without sacrificing static type checking, it should be possible to describe types that OK as long as they contain a set of methods.
This type is described by structural types in Scala. The use of structural types is relatively simple, as long as you enclose the declaration of the desired method in {} where the type is declared.
Def foo (callable: {def call: Unit}) =.
In addition, in the case of a large number of enumerated methods, aliases can be defined as follows, so that instead of listing all the methods every time, you only need to use the alias of the structure type.
Type Callable = {def call: Unit}
Compound (Compound) type
When using Java, have you ever thought of using a type that inherits type An and implements interface B? In addition to the restrictions on type parameters, there are no methods defined for this type in Java. However, this type can be described very simply in Scala. The description method is to concatenate the additional types with with after the general class name.
For example, for the following variable f, anyone can assign a value to the variable f as long as it is an object that implements the java.io.Closeable and Readable interfaces.
Var f: java.io.Closeable with Readable =.. At this point, the study on "what are the Scala type systems and functions" is over. I hope to be able to solve your doubts. The collocation of theory and practice can better help you learn, go and try it! If you want to continue to learn more related knowledge, please continue to follow the website, the editor will continue to work hard to bring you more practical articles!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.