对重载方法的Scala泛型调用

时间:2014-12-10 20:32:51

标签: scala generics

我是Scala的新手 - 在使用Apache POI生成Excel文件时,我尝试通过此通用删除代码中的重复:

def addCell[A](
  row:       org.apache.poi.ss.usermodel.Row,
  idxColumn: Int,
  data:      A)
  :
  Unit = 
{
  row.createCell(idxColumn).setCellValue(data)
}

不幸的是,编译器报告:

[error] .../mycode.scala:32: overloaded method value setCellValue \
with alternatives:
[error]   (x$1: Boolean)Unit <and>
[error]   (x$1: String)Unit <and>
[error]   (x$1: org.apache.poi.ss.usermodel.RichTextString)Unit <and>
[error]   (x$1: java.util.Calendar)Unit <and>
[error]   (x$1: java.util.Date)Unit <and>
[error]   (x$1: Double)Unit
[error]  cannot be applied to (A)
[error]     row.createCell(idxColumn).setCellValue(data)
[error]                               ^
[error] one error found
[error] (compile:compile) Compilation failed

我无法得到它 - 无论谁调用addCell,都会传递特定类型A(第三个参数的类型data),因此通用形式应该是能够发送到setCellValue的正确重载形式。

我可能遗漏了一些明显的东西 - 任何最感谢的帮助。

编辑:为了说清楚 - 我的代码目前正在执行此操作:

field match {
  case i:Int => row.createCell(idxCol).setCellValue(i)
  case l:Long => row.createCell(idxCol).setCellValue(l)
  case f:Float => row.createCell(idxCol).setCellValue(f)
  case d:Double => row.createCell(idxCol).setCellValue(d)
  case s:String => row.createCell(idxCol).setCellValue(s)
      ...

我希望通过对

之类的通用调用来避免明显的重复
field match {
  case i:Int => setCell(row, idxCol, i)
  case l:Long => setCell(row, idxCol, l)
  case f:Float => setCell(row, idxCol, f)
  ...

有可能吗?

2 个答案:

答案 0 :(得分:4)

最直接的方法是为每种类型编写一个单独的方法:

def addCell(
  row:       org.apache.poi.ss.usermodel.Row,
  idxColumn: Int,
  data:      Boolean)
  :
  Unit = 
{
  row.createCell(idxColumn).setCellValue(data)
}

def addCell(
  row:       org.apache.poi.ss.usermodel.Row,
  idxColumn: Int,
  data:      String)
  :
  Unit = 
{
  row.createCell(idxColumn).setCellValue(data)
}

如果只需要addCell这样写就可以了。


如果您需要更多方法的代码,您可以考虑采用更高级的方法来限制每种方法的样板。

更高级方法的草图:

  1. 定义一个cellSetter trait,它可以为单元格设置给定类型的值。

    trait CellSetter[A]{
        def setCell(cell: Cell, data: A): Unit
    }
    
  2. 为所有相关类型实施CellSetter,例如:

    implicit val stringCellSetter = new CellSetter[String](){
      def setCell(cell: Cell, data: String){
        cell.setCellValue(data)
      }
    }
    
  3. 制作一些糖:

    implicit class RichCell(cell: Cell){
        def setCellValue[A](data: A)(implicit cellSetter: CellSetter[A]) = cellSetter.setCell(cell, data)
    }
    
  4. 为addCell创建一些不错的通用代码:

    def addCell[A: CellSetter](
      row:       org.apache.poi.ss.usermodel.Row,
      idxColumn: Int,
      data:      A)
      :
      Unit = 
    {
      row.createCell(idxColumn).setCellValue(data)
    }
    

答案 1 :(得分:1)

不是说我做过这样的事情,但我很好奇专业化是否有帮助。

最低限度,你想避免装箱原语。

事实证明,专业化并没有多大帮助,因为专门的方法不会派遣到非专业的方法。我想我以前读过这个问题。

在下文中,专门化add可以正常工作。一个版本使用ClassTag选择引用类型,或另一个版本处理基元。但是注释掉的代码只调用了setCellValue(Any);布尔版本调用setCellValue(Boolean)

是不正确的

评论的版本似乎会破坏后端......

import scala.reflect.ClassTag
import java.util.Date

trait Setter {
  def setCellValue(b: Boolean) = println("bool")
  def setCellValue(s: String) = println("str")
  def setCellValue(n: Double) = println("double")
  def setCellValue(d: Date) = println("date")
}

object Test extends App {
  def add[@specialized(Boolean, Double) A <: AnyVal](s: Setter, a: A) = {
    /*
    class ASetter extends Setter {
      override def setCellValue(b: Boolean) = s setCellValue b
      override def setCellValue(n: Double) = s setCellValue n
      def setCellValue(x: Any) = ???
    }
    (new ASetter) setCellValue a
    */
    a match {
      case b: Boolean => s setCellValue b
      case d: Double  => s setCellValue d
      case _ => ???
    }
  }
  def add[A: ClassTag](s: Setter, a: A) = a match {
    //case _: Boolean | _: Double => s setCellValue a
    case d: Date   => s setCellValue d
    case x: String => s setCellValue x
    case _ => ???
  }

  val s = new Setter {}
  add(s, new Date)
  add(s, true)
}