In Java, static members are not called by instance, but by class name, and the keyword is static. Strictly speaking, a static member does not satisfy the idea of OOP: it has nothing to do with the class per se. In other words, it exists more like a global variable of PO.

Due to the limitations of the time, the Java language had to accommodate some of the content of PO ideas. Scala, a multiparadigm language, takes OOP to the extreme in addition to FP. When designing the language, Martin Ordesky, the father of Scala, removed static, a concept that OOP regarded as a nuisance.

However, sometimes we do need a static global variable that is separate from a specific object. To compensate for the lack of static (or to make it look a little more OOP), Martin Odesky introduced the concept of companion objects: in Scala, classes “move and move.” All non-static content is stored in a class, while static content is stored in an Object.

This is why Scala’s main functions are declared inside an object: they are “static,” singletons. In addition, in some sources, also known as companion objects are singletons.

How do I declare a companion object

There is no static keyword in Scala’s world, and there is no concept associated with statics. However, given our experience with Java, I will use the term “static” to illustrate some of the concepts below.

An example of a companion object is given first, and we will go into the details.

object Associated_Object {
  def main(args: Array[String) :Unit= {}}class Associated_Object{}
Copy the code

If there is an object x declaration and a class x declaration in the same file:

  • Let’s call class x a companion class of Object x.

  • Its object x is called the companion object of class X.

Associated classes and associated objects are relative concepts.

At compile time, the companion class object x is compiled as x.class, and the companion object object x is compiled as x$.class.

Members declared in a companion object can be called directly by the class name. For example, declare a property in a companion object:

object Associated_Object {
  val publicKey: String = "Associated value"
}
Copy the code

So in the main function, we can call the value of publicKey directly with the class name:

println(s"args = ${Associated_Object.publicKey}")
Copy the code

Similarly, companion objects can declare private members. Thus, the decorated member can only be accessed by instances of the companion class, and all instances of the companion class share this variable.

The apply method of the companion class

Look at the code below. Where is the new keyword?

 val cat : Cat = Cat(a)Copy the code

This writing implicitly calls the apply method on its companion object. The full declaration of the Cat class is given below:

class Cat(){}

object Cat{
  // This method has constructors built in.
  def apply() : Cat = new Cat()}Copy the code

You can also think of it as a simple factory pattern: call the apply method on the Cat companion object and return an instance.

Note:

  • Declarations are only made in companion objectsapplyAfter the method, the follow-up may not be broughtnewKeywords create instances directly, which is Scala’s syntactic sugar.
  • applyMethods are allowed to carry parameters,The return value should in most cases be an instance of this class, but Scala doesn’t have a strict compilation perspective on this.

I strongly recommend following the second rule to avoid this confusion:

object Cat{
    // This code has no problems from a compilation point of view.
	def apply() : Dog = new Dog()}//------Main-----------//
// However, this confuses the caller of the code.
// In 99.9999% of cases, the apply method should return an instance of its corresponding class.
val dog : Dog = Cat 
Copy the code

In addition, Scala has an unapply method, or extractor. We will refer to it formally in the following section on Pattern matching.

Can I declare only the companion object, but not the companion class?

Of course! In fact, most of the time when we were writing cases, we just declared an object, and declared the main function logic directly inside.

From a compilation point of view, this is an interesting phenomenon:

X $.class: x$.class: x$.class: x$.class: x$.class Even if a companion class is not declared with a class, the compiler still generates an empty X.lass file underneath.

A class decorated with only one class will generate only one x.lass file at compile time. The following section shows how to implement Scala’s companion objects and classes in Java code, and this example will help you understand why Scala compiles two files.

Can we just rely on individual objects?

The companion object itself implements the singleton pattern. Therefore, some people also called the companion object is a singleton object, there are rules to follow. Such as:

object Single {
  def fun() :Unit =  println("this is a singleton.")}Copy the code

Thus, the Single symbol in our program refers to this singleton object and can be used. Operator directly calls internally exposed properties and methods.

The author verifies that inheritance relationship can be declared directly on singleton object. To compile, we will write the following Parent and Single inside a.scala file.

class Parent {
 
  def greet() : Unit = println("hello")}object Single extends Parent {

  def fun() :Unit =  println("this is a singleton.")}Copy the code

Thus, we can call the method it inherited from Parent directly with the single.greet () method. But obviously, declaring inheritance in an Object singleton is neither fish nor fish. Why is that? Because that’s not what companion objects are originally for.

However, the compiler “passes” this noncanonical code because there really is no problem from a compilation standpoint. But it can confuse beginners (the author), such as:

  1. The inheritance relationship and constructor are written inobjectAnd written in the bookclassIs there a difference?
  2. If so, what are the differences between them?
  3. If you’re in a pairobjectclassDeclare different inheritance relationships. Is this a multiple inheritance?

The author here through the actual use of code to verify one by one.

Singletons do not take a constructor

After a few modifications to the Single, I find that you cannot declare any constructors on singletons. The following is not acceptable:

object Single(val int : Int) {

  def fun() :Unit =  println("this is a singleton.")}Copy the code

The editor will report that the above code has syntax errors. This suggests that Martin Odesky, the designer of Scala, has put some compile-level restrictions on companion objects. If the Single class requires a custom constructor, it must rely on its class-modified companion class to do so:

object Single {def fun() :Unit =  println("this is a singleton.")}
class Single(val int : Int)
Copy the code

Beware of Scala’s smoke screen

How about declaring an inheritance relationship between class and object separately? I give the following “problem code” :(in order to compile, these class declarations need to be written in a.scala file)

class Father

object Single extends Father {

  def fun() :Unit =  println("this is a singleton.")}class Mother

class Single(val int : Int) extends Mother 
Copy the code

The compilation was successful! It looks like Single inherited both Father and Mother. Is that the truth?

Although the concept of companion object and companion class is used to decorate the “static” and “dynamic” parts of a class, Scala compasses companion objects and companion classes separately (i.e., the aforementioned x.lass and x$.class).

So they really just share a class name, and the companion object acts as a “repository” for the companion class by holding “static” variables and methods.

To avoid unnecessary confusion, when we use both associated objects and associated classes to describe a “class with static content,” we only put the “static” members on the object, and the inheritance, constructor, and other content declarations on the class.

Use a Java program to simulate the companion object implementation

To get a sense of how the compiler compiles companion objects and companion classes at the bottom level, let’s simulate it directly using Java code. Here we assume a companion class Associated_Object that has a static attribute of type Integer: publicKey. The member is declared in its companion object, Associated_Object$.

  1. Associated_Object$constructs an instance MODULE$in the static field. This instance uses the final keyword to protect it from memory changes.

  2. The Associated_Object class also has a static method getPublicKey: it always points to the publicKey in MODULE$.

Through the above two steps, this is equivalent to constructing a singleton pattern: any Associated_Object that needs to access the publicKey is fetched from the static field MODULE$.

The code implementation is shown below.

public final class Associated_Object$ {

    private Integer publicKey;

    // Specify MODULE$in the static field to hold the static properties of Associated_Object.
    static {
        MODULE$ = new Associated_Object$();
    }
	
    public static final Associated_Object$ MODULE$;

    public Integer getPublicKey(a) {
        return MODULE$.publicKey;
    }

    public void setPublicKey(Integer publicKey) {
        MODULE$.publicKey = publicKey;
    }

    // during initialization, the publicKey is assigned.
    // Static attributes of Associated_Object are stored in MODULE$with initialization.
    Associated_Object$() {
        this.publicKey = 100; }}//--------------------------------------------------------------------//
public class Associated_Object {

    private Integer InstanceKey;
	// Non-static attributes are stored in the Associated_Object class itself and can be called normally.
    public Integer getInstanceKey(a) {
        return InstanceKey;
    }

    public void setInstanceKey(Integer instanceKey) {
        InstanceKey = instanceKey;
    }
	
    // The Associate_Object class itself does not have a static "publicKey" attribute.
    // So we need to delegate an instance of Associated_Object$to get the corresponding attribute from MODULE$.
    public static Integer getPublicKey(a) {
        return Associated_Object$.MODULE$.getPublicKey();
    }
	// The principle is the same as getPublicKey.
    public static void setPublicKey(Integer salary) { Associated_Object$.MODULE$.setPublicKey(salary); }}Copy the code

summary

In this section, we looked at the relationship between companion objects and companion classes in Scala:

  1. In Scala, the “static” members of a class are essentially compiled in a separate.classIn the.
  2. The name of the companion object and the name of the companion class must be the same.scalaFile in order to compile correctly.

With the implementation of Java code, “static” in the Scala world is just a smoke screen. When we call “static” content, we’re actually calling an Object singleton (or companion object, but it’s better to call it a singleton here), which has nothing to do with the companion class.

However, since the companion object and the companion class have the same name, we can call it with an uppercase “class name” and, according to the Habit of learning Java, assume it is a “static” member of the Single class.

However, Scala does not stipulate that companion objects and companion classes must come in pairs. We can define only class or only object. Using only one object is best when you only need to implement a simple singleton pattern: an entry to a main function, for example.

A profound

First, define a Counter class that has a static attribute count. When the main function is started, the count is incremented once every Counter instance is instantiated.

The following code is a Java implementation.

public class Counters{

    private static int count = 0;

    public Counters(a){ count++; }}Copy the code

This requirement is implemented in Scala as follows:

class Counter {
  Counter.count += 1
}

object Counter {
  var count : Int = 0
}
Copy the code

Don’t forget one thing: Companion objects and companion classes in Scala are separate from each other from a compilation perspective. So we can’t access count directly in the companion class, but instead take the name of the companion object (although they are all called the same name) : counter.count.