Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to understand the functions in Scala

2025-01-23 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

This article mainly explains "how to understand the functions in Scala". The content of the explanation is simple and clear, and it is easy to learn and understand. Please follow the editor's train of thought to study and learn "how to understand functions in Scala".

Ordinary Scala object

Similar to previous articles in this series, I'll use the Person class as a starting point to explore Scala's inheritance system. Listing 1 shows the class definition of Person:

Listing 1. Hey, I'm human.

/ / This is Scala class Person (val firstName:String, val lastName:String, val age:Int) {def toString = "[Person: firstName=" + firstName+ "lastName=" + lastName+ "age=" + age+ "]"}

Person is a very simple POSO (normal Scala object, Plain Old Scala Object) with three read-only fields. You may recall that to make these fields readable and writable, simply change the val in the main constructor declaration to var.

In any case, using the Person type is very simple, as shown in listing 2:

Listing 2. PersonApp

/ This is Scala object PersonApp {def main (args: Array [String]): Unit = {val bindi = new Person ("Tabinda", "Khan", 38) System.out.println (bindi)}}

This is not much of a surprise code, but it gives us a starting point.

Abstract methods in Scala

As the system evolves, it becomes more and more obvious that Person classes lack an important part of Person, which is the act of doing something. Many of us define ourselves by what we do in our lives, not by the space we have and occupy. So, I'll add a new method, as shown in listing 3, which gives Person some meaning:

Listing 3. Good, do something!

/ / This is Scala class Person (val firstName:String, val lastName:String, val age:Int) {override def toString = "[Person: firstName=" + firstName+ "lastName=" + lastName+ "age=" + age+ "]" def doSomething = / / uh.... What?}

This raises the question: what exactly is the purpose of Person? Some Person paint, some sing, some write code, some play video games, some do nothing (ask the parents of teenagers). So, instead of trying to integrate these activities directly into the Person itself, I will create subclasses for Person, as shown in listing 4:

Listing 4. This man does very little.

/ / This is Scala class Person (val firstName:String, val lastName:String, val age:Int) {override def toString = "[Person: firstName=" + firstName+ "lastName=" + lastName+ "age=" + age+ "]" def doSomething = / / uh.... What?} class Student (firstName:String, lastName:String, age:Int) extends Person (firstName, lastName, age) {def doSomething = {System.out.println ("I'm studying hard, Ma, I swear! (Pass the beer, guys!")}}

When I tried to compile the code, I found that it could not be compiled. This is because the definition of the Person.doSomething method does not work; this method requires a full body (or permission to throw an exception to indicate that it should be overridden in an inherited class), or no body, similar to how abstract methods work in Java code. I tried to use an abstract method in listing 5:

Listing 5. Abstract class Person

/ / This is Scala abstract class Person (val firstName:String, val lastName:String, val age:Int) {override def toString = "[Person: firstName=" + firstName+ "lastName=" + lastName+ "age=" + age+ "]" def doSomething / / note the semicolon, which is still optional / / but stylistically I like having it here} class Student (firstName:String, lastName:String, age:Int) extends Person (firstName, lastName, age) {def doSomething = {System.out.println ("I'm studying hard, Ma, I swear! (Pass the beer, guys!) ")}}

Notice how I decorate the Person class with the abstract keyword. Abstract points out to the compiler that, yes, this class should be abstract. In this respect, the Scala language is no different from the Java language.

Object, encountered a function

Because Scala combines object and functional language styles, I actually modeled Person (as described above), but did not create subtypes. This is a bit odd, but it emphasizes Scala's integration of the two design styles, and the interesting ideas that come with it.

Recalling previous articles, Scala treats functions as values, just like other values in the language, such as Int, Float, or Double. When modeling Person, I can take advantage of this to get doSomething, not only as a method to inherit overrides in classes, but also as function values that can be called, replaced, and extended. Listing 6 shows this approach:

Listing 6. A person who works hard

/ / This is Scala class Person (val firstName:String, val lastName:String, val age:Int) {var doSomething: (Person) = > Unit = (p:Person) = > System.out.println ("Isimm" + p + "and I don't do anything yet!") Def work () = doSomething (this) override def toString = "[Person: firstName=" + firstName+ "lastName=" + lastName+ "age=" + age+ "]"} object App {def main (args: Array [String]) = {val bindi = new Person ("Tabinda", "Khan") 38) System.out.println (bindi) bindi.work () bindi.doSomething = (p:Person) = > System.out.println ("I edit textbooks") bindi.work () bindi.doSomething = (p:Person) = > System.out.println ("I write HTML books") bindi.work ()}}

Using functions as modeling tools is a common skill in dynamic languages such as Ruby, Groovy, and ECMAScript (that is, JavaScript), as well as in many functional languages. Although functions can be used as modeling tools in other languages (C++ through function pointers and / or member function pointers, and anonymous inner classes referenced by interfaces in Java code), much more work is required than Scala (and Ruby, Groovy, ECMAScript, and other languages). This is an extension of the concept of "higher-order functions" used in functional languages.

Thanks to Scala treating functions as values, you can take advantage of function values when you need to switch functions at run time. Think of this approach as a variant of the role pattern, the Gang of Four strategic model, in which object roles (such as the current inaugural state of Person) perform better as runtime values and better than the hierarchy of static types.

The constructor of the upper layer of the hierarchy

Recall the days when Java code was written, and sometimes inherited classes need to pass parameters from the constructor to the base class constructor so that the base class fields can be initialized. In Scala, because the main constructor appears in the class declaration and is no longer a "traditional" member of the class, passing parameters to the base class becomes an entirely new dimension.

In Scala, the parameters of the main constructor are passed on the class line, but you can also use the val modifier for these parameters to easily introduce a value reader (or, in the case of var, a writer) on the class itself.

Therefore, the Scala class Person in listing 5 is transformed into the Java class in listing 7, using javap to see:

Listing 7. Please translate it.

/ / This is javap C:\ Projects\ scala-inheritance\ code > javap-classpath classes Person Compiled from "person.scala" public abstract class Person extends java.lang.Object implements scala.ScalaObje ct {public Person (java.lang.String, java.lang.String, int); public java.lang.String toString (); public abstract void doSomething (); public int age (); public java.lang.String lastName (); public java.lang.String firstName (); public int $tag ();}

The basic rule of JVM still holds: Person's inherited class passes something to the base class at construction time, regardless of what the language emphasizes. In fact, this is not entirely true, but JVM behaves abnormally when languages try to circumvent this rule, so most languages still insist on supporting it in some way. Of course, Scala needs to stick to this rule, because it needs not only to keep JVM working, but also to keep the Java base class working. That is, in any case, Scala must implement a syntax that allows the inherited class to call the base class, while retaining the syntax that allows us to introduce readers and writers on the base class.

To put this into a more specific context, suppose I wrote the Student class in listing 5 in the following ways:

Listing 8. Bad student!

/ / This is Scala / / This WILL NOT compile class Student (val firstName:String, val lastName:String, val age:Int) extends Person (firstName, lastName, age) {def doSomething = {System.out.println ("I'm studying hard, Ma, I swear! (Pass the beer, guys!)")}}

The compiler in this example will run for a long time because I'm trying to introduce a new set of methods (firstName, lastName, and age) for the Student class. These methods will conflict with methods with similar names on the Person class, and the Scala compiler doesn't necessarily know if I'm trying to override base class methods (which is bad, because I can hide implementations and fields behind these base class methods), or introduce new methods with the same name (which is also bad, because I can hide implementations and fields after these base class methods). In short, you'll see how to successfully override methods from the base class, but that's not what we're pursuing right now.

You should also note that in Scala, the parameters of the Person constructor do not have to be one-to-one associated with the parameters passed to Student; the rules here are actually exactly the same as those of the Java constructor. We only do this to make it easier to read. Similarly, Student can require additional constructor parameters, as in the Java language, as shown in listing 9:

Listing 9. Demanding student!

/ This is Scala class Student (firstName:String, lastName:String, age:Int, val subject:String) extends Person (firstName, lastName, age) {def doSomething = {System.out.println ("I'm studying hard, Ma, I swear! (Pass the beer, guys!)")}}

Once again, you see how similar Scala code is to Java code, at least when it comes to inheritance and class relationships.

Grammatical differences

At this point, you may be confused about the details of the grammar. After all, Scala does not distinguish fields from methods as the Java language does. This is actually a thoughtful design decision that allows Scala programmers to easily "hide" the differences between fields and methods from users who use the base class. Consider listing 10:

Listing 10. What am I?

/ This is Scala abstract class Person (val firstName:String, val lastName:String, val age:Int) {def doSomething def weight: Int override def toString = "[Person: firstName=" + firstName+ "lastName=" + lastName+ "age=" + age+ "]"} class Student (firstName:String, lastName:String, age:Int, val subject:String) extends Person (firstName, lastName Age) {def weight: Int = age / / students are notoriously skinny def doSomething = {System.out.println ("I'm studying hard, Ma, I swear! (Pass the beer, guys!))}} class Employee (firstName:String, lastName:String, age:Int) extends Person (firstName, lastName, age) {val weight: Int = age * 4 / / Employees are not skinny at all def doSomething = {System.out.println ("I'm working hard, hon, I swear! (Pass the beer, guys!) ")}}

Notice how to define weight so that it doesn't take any parameters and returns Int. This is the "no parameter method". Because it looks very similar to the "proprietary" method in the Java language, Scala actually allows you to define weight as a method (as shown in Student) and as a field / accessor (as shown in Employee). This syntactic decision gives you some flexibility in the implementation of abstract class inheritance. Note that in Java, even within the same class, similar flexibility can only be achieved when fields are accessed through the get/set method. I don't know whether the judgment is correct or not, but I think only a few Java programmers will write code in this way, so flexibility is not often used. In addition, Scala's approach can deal with hidden / private members as easily as public members.

From @ Override to override

Inherited classes often need to change the behavior of methods defined in one of their base classes; in Java code, we deal with this by adding new methods with the same name and signature to the inherited class. The disadvantage of this approach is that an error or ambiguity in the signature entry can lead to an asymptomatic failure, which means that the code can be compiled but cannot complete the operation correctly at run time.

To solve this problem, the Java 5 compiler introduced the @ Override annotation. @ Override verifies that the method that introduces the inherited class actually overrides the base class method. In Scala, override has become part of the language, and you can almost forget that it generates compiler errors. Therefore, inheriting the toString () method should look like listing 11:

Listing 11. This is the result of inheritance.

/ / This is Scala class Student (firstName:String, lastName:String, age:Int, val subject:String) extends Person (firstName, lastName, age) {def weight: Int = age / / students are notoriously skinny def doSomething = {System.out.println ("I'm studying hard, Ma, I swear! (Pass the beer, guys!))} override def toString = "[Student: firstName=" + firstName+ "lastName=" + lastName+ "age=" + age+ "subject=" + subject+ "]"}

It's very simple and clear.

Finalize

Of course, the flip side of allowing inheritance overrides is to take steps to prevent it: the base class needs to prohibit subclasses from changing their base class behavior, or to prohibit any type of inherited class. In the Java language, we do this by applying the modifier final to the method to ensure that it is not overridden. In addition, you can apply final to the class as a whole to prevent inheritance. The effect of the implementation hierarchy is the same in Scala: we can apply final to the method to prevent subclasses from overwriting it, or to the class declaration itself to prevent inheritance.

Keep in mind that all this discussion of abstract, final, and override applies equally to "methods with interesting names" (as Java or C# or C++ programmers might call operators), just as they apply to regular name methods. As a result, we often define a base class or feature that sets certain expectations for mathematical functions (called "Mathable") that define abstract member functions "+", "-", "*" and "/", as well as other mathematical operations that should be supported, such as pow or abs. Other programmers can then create other types-perhaps a Matrix class that implements or extends "Mathable" and defines members that look like the built-in arithmetic types provided by Scala out of the box.

The difference is that.

If Scala can map so easily to the Java inheritance model (as you've seen so far in this article), you should be able to inherit Scala classes from the Java language, or vice versa. In fact, this must be feasible because Scala, like other languages compiled into Java bytecode, must generate objects that inherit from java.lang.Object. Note that the Scala class may also inherit from other things, such as features, so the actual inheritance parsing and code generation may work differently, but eventually we must be able to inherit the Java base class in some form. Keep in mind that features are similar to behavioral interfaces, and the Scala compiler divides the features into interfaces and pushes the implementation into the target class compiled by the features, making it work in this way. )

However, the results show that the type hierarchy of Scala is slightly different from the corresponding structure in the Java language; technically, the base classes inherited by all Scala classes (including Int, Float, Double, and other numeric types) are scala.Any types, which defines a set of core methods that can be used on any type within Scala: = =,! =, equals, hashCode, toString, isInstanceOf, and asInstanceOf, most of which can be easily understood by name. Here, Scala is divided into two branches, and "primitive type" inherits from scala.AnyVal; "class type" inherits from scala.AnyRe. (scala.ScalaObject inherits from scala.AnyRef. )

Usually, this is not something you have to worry about directly, but it can have some very interesting side effects when considering inheritance across two languages. For example, consider ScalaJavaPerson in listing 12:

Listing 12. Mix!

/ / This is Scala ass ScalaJavaPerson (firstName:String, lastName:String, age:Int) extends JavaPerson (firstName, lastName, age) val weight: Int = age * 2 / / Who knows what Scala/Java people weigh? Override def toString = "[SJPerson: firstName=" + firstName+ "lastName=" + lastName+ "age=" + age+ "]"

…… It inherits from JavaPerson:

Listing 13. Does it look familiar?

/ / This is Java public class JavaPerson {public JavaPerson (String firstName, String lastName, int age) {this.firstName = firstName; this.lastName = lastName; this.age = age;} public String getFirstName () {return this.firstName;} public void setFirstName (String value) {this.firstName = value } public String getLastName () {return this.lastName;} public void setLastName (String value) {this.lastName = value;} public int getAge () {return this.age;} public void setAge (int value) {this.age = value } public String toString () {return "[Person: firstName" + firstName + "lastName:" + lastName + "age:" + age + "]";} private String firstName; private String lastName; private int age;}

When compiling ScalaJavaPerson, it will extend JavaPerson as usual, but it will also implement the ScalaObject interface as required by Scala. And support methods inherited from JavaPerson as usual, because ScalaJavaPerson is a Scala type, we can expect it to support the assignment of Any references, according to Scala rules:

Listing 14. Use ScalaJavaPerson

/ This is Scala val richard = new ScalaJavaPerson ("Richard", "Campbell", 45) System.out.println (richard) val host: Any = richard System.out.println (host)

But what happens when you create a JavaPerson in Scala and assign it to an Any reference?

Listing 15. Use JavaPerson

/ This is Scala val carl = new JavaPerson ("Carl", "Franklin", 35) System.out.println (carl) val host2: Any = carl System.out.println (host2)

It turns out that this code compiles and runs as scheduled, because Scala ensures that JavaPerson "does the right thing" thanks to the similarity between the Any type and the java.lang.Object type. In fact, almost all content that extends java.lang.Object supports storage in Any references. There are some extreme cases, I have heard of them, but I have never encountered such extreme cases myself. )

The end result? For practical purposes, we can inherit across the Java language and Scala mashup without worrying too much.

Thank you for your reading, the above is the content of "how to understand the functions in Scala". After the study of this article, I believe you have a deeper understanding of how to understand the functions in Scala, and the specific use needs to be verified in practice. Here is, the editor will push for you more related knowledge points of the article, welcome to follow!

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: 242

*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.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report