Scala programming--functional objects

Source: Internet
Author: User
Tags greatest common divisor

The focus of this chapter is to define a functional object, that is, the class of an object without any mutable state. As an example of running, we will create variants of several classes that model fractions as immutable objects. In this process, we will show you more aspects of Scala object-oriented programming: class parameters and constructors, methods and operators, private members, subclass method overloads, prerequisite checks, homogeneous method overloads, and self-pointing.

Type 6.1 Rational Sample booklet

One, perhaps less important, is that mathematically, fractions do not have a mutable state. A score is added to another score, and the result is a new score. And the original number will not be "changed". The immutable rational class that we will design in this chapter will uphold this attribute. Each score will be represented as a rational object. When two rational objects are added, a new rational object with cumulative results is created.

This chapter will also take you through some of Scala's methods for writing libraries that feel like native language support. For example, at the end of this chapter you will be able to do this with the rational class:

1scala> val onehalf =NewRational (1,2) 2Onehalf:rational =1/2 3scala> val twothirds =NewRational (2,3) 4Twothirds:rational =2/35Scala> (Onehalf/7) + (1twothirds)6Res0:rational = -/ the
View Code

6.2 Creating rational

The starting point to start designing the rational class is to consider how the client programmer will create a new rational object. Assuming that we have decided to make the rational object immutable, we will need that customer to provide all the required data (in this case, numerator and denominator) when creating the instance. So we should start by designing:

1 class Rational (N:int, D:int)
View Code

The first thing you should notice in this line of code is that if the class doesn't have a body, you don't need to specify a pair of empty braces (you can, of course, if you want to). In the class name, Rational, followed by the parentheses of N and D, is called the class parameter: Classes parameter. The Scala compiler collects these two class parameters and creates a main constructor with the same two parameters: primary constructor.

Note that the original rational example highlights the difference between Java and Scala. Java classes have constructors that can take parameters, and Scala classes can take parameters directly. Scala is more concise-the class parameters can be used directly in the body of the class; There is no need to define a field and then write the assignment function to copy the constructor parameters into the field. This can potentially save a lot of fixed notation, especially for small classes.

The Scala compiler will compile any code that you place inside the class that is not part of a field or method definition into the main constructor. For example, you can print out a single error message like this:

1 class  2 println ("+n+"/"+3  }
View Code

According to this code, the Scala compiler will place the println call in Rational's main constructor. Therefore, the println call will print this error message each time a new rational instance is created:

1 New Rational (12)21/23 res0:rational = [email protected]
View Code

6.3 To re-implement the ToString method

In the preceding example, when the rational instance is created, the interpreter prints out "[email protected]". The interpreter is this seemingly playful string that is obtained by invoking the ToString method of the rational object. By default, the rational class inherits the ToString implementation defined on the Java.lang.Object class, just printing the class name, an @ sign, and a hexadecimal number. The result of ToString is to try to help the programmer by providing information that can be used to print, log messages, test error reports and interpreters, and debug the output of a debugger. The result currently provided by ToString is not particularly useful because it does not give any clue to the rational value that it is invoking. A more useful ToString implementation should print out the numerator and denominator of rational. You can overload by adding the ToString method to the Rational class: Override the default implementation, such as:

1 class  2override def toString = n +"/"+3 }
View Code

The override modifier before the method definition indicates that the previous method definition is overloaded, and the 10th chapter explains it further. Now the score is pretty good, so we've removed the println in the previous version of the Rational class. You can test the new behavior of rational in the interpreter.

1 New Rational (1321/33new Rational (5  745/7
View Code

6.4 Checking Prerequisites

Next, we will turn our gaze to some problems in the current main constructor behavior. As mentioned earlier in this chapter, the denominator of a fraction cannot be zero. However, the main constructor will now accept the passing of zero to D:

1 New Rational (5025/0
View Code

One advantage of object-oriented programming is that it allows you to encapsulate data within objects so that you can ensure that the data is valid throughout the life cycle. Immutable objects like rational, which means that you have to make sure that the data is valid when the object is created (and that the object is indeed immutable so that the data does not become invalid later). Since zero does not have an invalid state for rational, it is important to not let rational be constructed when passing zeros to D.

The best way to solve this problem is to define a prerequisite for the primary constructor: Precondition Description D must be a non-0 value. A prerequisite is a restriction on the value passed to a method or constructor, which is a requirement that the caller must satisfy. One way is to use the Require method:

1 class  2     03     override def toString = n +"/  "+4 }
View Code

The Require method takes a Boolean parameter. If the value passed in is true, require will return normally. Conversely, require will prevent objects from being constructed by throwing illegalargumentexception.

6.5 Adding fields

Now that the main constructor can perform the prerequisites correctly, we will focus on supporting addition. To do this, we will define a public add method on the class rational, which takes another rational parameter. To keep rational immutable, the Add method must not add the incoming score to itself. Instead, you must create and return a new rational with an accumulated value. You might think you could write this. Add:

1 class //  2     override def toString = n +"/"+3     New Rational (n * that.d + THAT.N * d, D *4 }
View Code

Unfortunately, the code above will cause the compiler to prompt that:

1 <console>:One isnew Rational (n * that.d + THAT.N * d, D *2 &L T;console>: Oneisnew Rational (n * that.d + THAT.N * d, D * that.d)
View Code

Although the class arguments N and D are within the scope that your add code can reference, only their values can be accessed in the object that calls Add. Therefore, when you speak N or D in the implementation of add, the compiler will gladly provide you with values for these class parameters. But it's never going to let you use THAT.N or THAT.D, because that doesn't point to the rational object that add is called. To access the N and d of that, you need to put them in a field. Code 6.1 shows how to add these fields to the class rational.

In the rational version shown in code 6.1, we added two fields, Numer and Denom respectively, and initialized them with class parameters N and D. We have also changed the implementation of ToString and add so that they use fields instead of class parameters. This version of the class rational can be compiled and tested by the addition of fractions:

1 classRational (N:int, d:int) {2Require (d! =0) 3Val Numer:int =N4Val Denom:int =D5     Overridedef toString = numer+"/"+Denom6def add (that:rational): Rational =NewRational (Numer * that.denom + that.numer * denom, Denom *that.denom)7}
View Code
 1  scala> val onehalf = new  Rational (1 , 2  )  2  onehalf:rational = 1 /2  3  scala> val twothirds = Span style= "color: #0000ff;" >new  Rational (2 , 3  )  4  twothirds:rational = 2 /3  5  scala> onehalf add Twothirds  6  res0:rational = 7 /6  
View Code

Another thing that can't be done now is to access the numerator and denominator outside of the object. Simply access the public Numer and Denom fields:

1 New Rational (122123 scala>4 1 5 scala>62
View Code

6.6 Self-pointing

The keyword this points to an instance of an object that is called by the current execution method, or if it is used in a constructor, is the object instance being constructed. For example, we consider adding a method, LessThan, to test whether a given fraction is less than the passed-in parameter:

1  This this. denom
View Code

Here, This.numer points to the molecule of the object that LessThan was called. You can also remove this prefix instead of just writing numer; two of the same notation. To take an example that cannot be absent, consider adding the Max method in the rational class to return the larger of the specified fractions and parameters:

1 if (Thiselse this
View Code

Here, the first this is redundant, and you write (LessThan) is the same. But the second this represents the result of the method when the test is false; If you omit it, nothing will go back.

6.7 from the constructor

There are times when multiple constructors are needed in a class. A constructor outside the main constructor in Scala is called from the constructor: Auxiliary constructor. For example, a fraction of a denominator of 1 is more concise when the numerator is written. For example, for 5/1, you can just write 5. Therefore, if it is not written as rational (5, 1), it may be better for a client programmer to simply write rational (5). This requires rational to add a single parameter to the numerator, the constructor and pre-set the denominator to 1. Code 6.2 shows what it should look like.

1 classRational (N:int, d:int) {2Require (d! =0) 3Val Numer:int =N4Val Denom:int =D5Def This(n:int) = This(N,1) 6     Overridedef toString = numer+"/"+Denom7def add (that:rational): Rational =NewRational (Numer * that.denom + that.numer * denom, Denom *that.denom)8}
View Code

Scala starts with the Def this (...) from the constructor. Rational's from the constructor body is almost entirely called the main constructor, which directly passes its unique parameters, N, as numerator and 1 as the denominator. Enter the following code into the interpreter to actually see the effect from the constructor:

1 New Rational (323/1
View Code

Each of the first actions in Scala from the constructor is to call the other constructors in the same class. In other words, each of the constructors in the Scala class is "this (...)" The beginning of the form. The called constructor can be either the main constructor (as in the case of rational) or the other slave constructors that call the constructor earlier than the one in the text. The fundamental result of this rule is that every Scala constructor call ends with a call to the class's main constructor. Therefore, the primary constructor is the only entry point for the class.

If you're familiar with Java, you might wonder why the rules of the Scala constructor are larger than Java's. In Java, the first action of a constructor must either call another constructor in the same class or call the constructor of the superclass directly. Within the Scala class, only the main constructor can call the superclass's constructor. The tighter restrictions in Scala are actually a design that balances higher simplicity with the cost of simplicity compared to the Java builder. The details of the superclass, constructor invocation, and inheritance interaction are explained in chapter 10th.

6.8 Private fields and methods

In the previous version of rational, we just initialized the Numer with N and initialized the denom with D. As a result, rational's numerator and denominator may be larger than it needs to be. For example, fraction 66/42, which can be simplified to the same simplest form, 11/7, but Rational's main constructor does not currently do this work:

1 New Rational (2
View Code

To simplify the score, you need to divide the numerator and denominator by greatest common divisor: greatest common divisor. such as: 66 and 42 of the greatest common divisor is 6. (another argument is that 6 is the largest integer capable of removing 66 and 42.) The numerator and denominator of 66/42 are divided by 6 to produce its simplest form, 11/7. Code 6.3 shows how to do this:

Class Rational (N:int, D:int) {require (d! = 0) Private val g = gcd (N.abs, d.abs) val numer = n/g val denom = d/g def This (n:int) = This (n, 1) def add (that:rational): rational = new Rational (numer * that.denom + that.numer * denom, Deno M * that.denom) override def toString = numer+ "/" +denom private def gcd (A:int, b:int): Int = if (b = = 0) A else gcd (b, A% B)}

  

In this version of rational, we added a private field, G, and modified the initializer for Numer and denom (initializer: initializer is the initialization variable, such as the code that initializes the Numer "n/g"). Because G is private, it can only be within the body of the class and cannot be accessed externally. We also added a private method, GCD, to calculate the greatest common divisor of the incoming two int. For example, GCD (12, 8) is 4. As you can see in section 4.1, to privatize a field or method you just put the private keyword in front of the definition. The purpose of the private "helper method" GCD is to separate the other parts of the class, which are the main constructors, and the code that is needed. To ensure that G is always positive, we pass in the absolute value of N and D, and call abs to get the absolute value of any integer.
The Scala compiler puts the initialization code for rational's three fields into the main constructor in the order in which they appear in the source code. So the initialization code of G, GCD (N.abs, D.abs), will be executed before the other two, because it appears earliest in the source file. G will be initialized to the greatest common divisor of the absolute value of the class parameters, N and D. It is then used for initialization of Numer and Denom. by dividing N and D by their greatest common divisor, g, each rational will be constructed into its simplest form.

1 New Rational (2)  /7
View Code

6.9 Defining operators

The current implementation of rational addition is not a problem in terms of complete functionality, but it can be done better. You may ask yourself why you can write for integers or floating-point numbers:

X + y

But if it is a score, it must be written as:

X.add (y)

Or at least:

X add y

There is no reasonable explanation why this must be the case. The score and the other number should be the same. Mathematically they are even more natural than, um, floating point numbers. Why can't you use a natural math operator? You get it in Scala. Later in this chapter, we'll show you how to do it. The first step is to replace the Add method with the usual mathematical notation. This can be done directly, because Scala + is a valid identifier. We can define the method name with the +. Now that you're here, you can also implement a * method to achieve multiplication, and the results are shown in code 6.4:

Scala programming--functional objects

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.