Pergunta

Eu gostaria de adicionar um método para uma (por exemplo Duplo) built-in tipo, para que eu possa usar um operador infix. Isso é possível?

Foi útil?

Solução

Sim e não. Sim, você pode fazê-lo parecem como você adicionou um método para double. Por exemplo:

class MyRichDouble(d: Double) {
  def <>(other: Double) = d != other
}

implicit def doubleToSyntax(d: Double) = new MyRichDouble(d)

Este código adiciona o operador <> anteriormente indisponíveis para qualquer objeto do tipo Double. Enquanto o método doubleToSyntax está no escopo para que pudesse ser invocado sem qualificação, o seguinte irá funcionar:

3.1415 <> 2.68     // => true

O "não" parte da resposta vem do fato de que você não está realmente acrescentar nada para a classe Double. Em vez disso, você está criando uma conversão de Double a um novo tipo que não definem o método que você deseja. Esta pode ser uma técnica muito mais poderoso do que as classes abertas oferecidos por muitas linguagens dinâmicas. Também acontece de ser completamente tipo seguro. : -)

Algumas limitações que você deve estar ciente de:

  • Esta técnica não permitem que você remover ou Redefinir métodos existentes, basta adicionar novos
  • O método de conversão implícita (neste caso, doubleToSyntax) tem absolutamente de ser dentro do escopo do método de extensão desejado para estar disponível

Idiomaticamente, conversões implícitas ou são colocados dentro de objectos únicos e importadas (por exemplo import Predef._) ou dentro de traços e herdadas (por exemplo class MyStuff extends PredefTrait).

Leve de lado: "operadores infix" em Scala são realmente métodos. Não há mágica associada com o método <> que permite que ele seja infix, o analisador simplesmente aceita-lo dessa maneira. Você também pode usar "métodos regulares" como operadores infixas se quiser. Por exemplo, a classe Stream define um método take que leva um único parâmetro Int e retorna uma nova Stream. Isso pode ser usado da seguinte maneira:

val str: Stream[Int] = ...
val subStream = str take 5

A expressão str take 5 é literalmente idêntico ao str.take(5).

Outras dicas

Este recurso veio a calhar para implementar uma classe realizar estimativa de erro:

object errorEstimation {
  class Estimate(val x: Double, val e: Double) {
    def + (that: Estimate) =
      new Estimate(this.x + that.x, this.e + that.e)
    def - (that: Estimate) =
      new Estimate(this.x - that.x, this.e + that.e)
    def * (that: Estimate) =
      new Estimate(this.x * that.x,
                   this.x.abs*that.e+that.x.abs*this.e+this.e*that.e)
    def / (that: Estimate) =
      new Estimate(this.x/that.x,
                   (this.x.abs*that.e+that.x.abs*this.e)/(that.x.abs*(that.x.abs-that.e)))
    def +- (e2: Double) =
      new Estimate(x,e+e2)
    override def toString =
      x + " +- " + e
  }
  implicit def double2estimate(x: Double): Estimate = new Estimate(x,0)
  implicit def int2estimate(x: Int): Estimate = new Estimate(x,0)

  def main(args: Array[String]) = {
    println(((x: Estimate) => x+2*x+3*x*x)(1 +- 0.1))
    // 6.0 +- 0.93
    println(((x: Estimate) => (((y: Estimate) => y*y + 2)(x+x)))(1 +- 0.1))
    // 6.0 +- 0.84
    def poly(x: Estimate) = x+2*x+3/(x*x)
    println(poly(3.0 +- 0.1))
    // 9.33333 +- 0.3242352
    println(poly(30271.3 +- 0.0001))
    // 90813.9 +- 0.0003
    println(((x: Estimate) => poly(x*x))(3 +- 1.0))
    // 27.037 +- 20.931
  }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top