简体   繁体   中英

Utility methods for operating on custom scala class

I'd like to define an operator that works on a custom class in Scala. Similar to scala's Array utility methods , such as Array concatenation:

val (a, b) = (new Array[Int](4), new Array[Int](3))
val c = Array.concat(a, b)

I'd like to define an operator vaguely as follows:

class MyClass {
  def op():MyClass = {
     // for instance,, 
     return new MyClass();
  }
}

to be invoked, like val x = MyClass.op()

To provide a more concrete example, suppose that MyClass is an extension of MyAbstractClass

// Provided as a utility for the more relevant code below. 
def randomBoolean():Boolean = {
  val randomInt = Math.round(Math.random()).toInt
  if (randomInt == 1 ) return true;
  else return false;
}

abstract class MyAbstractClass[T](size:Int) {
  val stuff = new Array[T](size)
  def randomClassStuff():Array[T]
}

class MyClass(size:Int) extends MyAbstractClass[Boolean](size) {
  def randomClassStuff():Array[Boolean] = {
    return new Array[Boolean](size) map {x => randomBoolean()}
  } 
}

I realize that I could define an object called MyClass with a function called randomClassStuff defined in there, but I'd rather utilize abstract classes to require that extensions of the abstract class provide a method that creates random stuff specific to that class .

Using your first example, to be able to invoke an operator concat as Array.concat() , there needs to exist an object called Array somewhere in scope that has concat as a method. The simplest way to do that would be:

object Array {
  def concat[T](xs: Array[T], ys: Array[T]): Array[T] = ...
}

Otherwise you could define an abstract class (or trait) and have the object inherit it:

abstract class ArrayUtils {
  // Implemented here
  def concat[T](xs: Array[T], ys: Array[T]): Array[T] = ...
}

//Define the object
object Arrays extends ArrayUtils

// Or as a val
val Array = new ArrayUtils {}

Alternatively the abstract class could just define the operator and the object would implement it:

abstract class ArrayUtils {
  // Defined here
  def concat[T](xs: Array[T], ys: Array[T]): Array[T]
}

object Arrays extends ArrayUtils {
  // Implemented here
  def concat[T](xs: Array[T], ys: Array[T]): Array[T] = ...
}

The last is probably closest to what you wanted.

We can define an OpTrait that declares a method ("op") that operates on something of the same type, so that any class that implements that method can only take in arguments of the same type as the class it was declared in.

Then, we can define an object that takes any two objects of the same type, as long as they include the "op" method (by mixing in the OpTrait--you can alternatively use structural typing, but that's slow and less organized):

trait OpTrait[B] {
    def op(b: B)
}

object MyClass {
    def op[A <: OpTrait[A]](x: A, y: A) = x.op(y) // x op y
}

Now we can create any class that extends OpTrait and class MyClass.op(obj1, obj2):

// example of usage
class MyClass(val num: Int) extends OpTrait[MyClass] {
    def op(b: MyClass) = new MyClass(num + b.num)
}

val (a,b) = (new MyClass(5), new MyClass(6))
MyClass.op(a, b) // should return MyClass(11)

Since the OpTrait is parameterized on a generic type, you can actually define an operation between different types (but only in one order). You can also define the operation to work on MyClass and a subtype of MyClass by making it covariant, but only when arguments are in one order.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM