Scala: Неабавязковы параметр тыпу?

Я напісаў метад, як гэта:

def typeOnly[T, S](seq: Seq[S]): Seq[T] = {
  seq.flatMap{
    case t: T => Some(t)
    case _ => None
  }
}

І я спадзяюся, што спасылацца на гэта наступным чынам:

typeOnly[String](List(1, "2", 3, "4"))

Гэта не працуе. Я, здаецца, павінен паказаць другі параметр тыпу S :

typeOnly[String, Any](List(1, "2", 3, "4"))

Але чаму? Калі не кампілятар ведае тое, што Спіс (1, "2", 3 "4") гэта Seq [Любы] ?

1
Ну, вы маеце рацыю. Ці можна перапісаць гэты метад з тыпам класа?
дададзена аўтар Lai Yu-Hsuan, крыніца
Ну, вы маеце рацыю. Ці можна перапісаць гэты метад з тыпам класа?
дададзена аўтар Lai Yu-Hsuan, крыніца
Кампілятар <�б> можа зрабіць выснову аб тым, што спіс (1, "2", 3 "4") з'яўляецца Seq [Любы] , але ён не можа жыць з думкай, што вы нядбала забыліся пазначыць другі тып. Звярніце ўвагу, што ваша адпаведнасць бескарысна, так як тып Seq сціраецца падчас выканання.
дададзена аўтар om-nom-nom, крыніца
Кампілятар <�б> можа зрабіць выснову аб тым, што спіс (1, "2", 3 "4") з'яўляецца Seq [Любы] , але ён не можа жыць з думкай, што вы нядбала забыліся пазначыць другі тып. Звярніце ўвагу, што ваша адпаведнасць бескарысна, так як тып Seq сціраецца падчас выканання.
дададзена аўтар om-nom-nom, крыніца
Калі вы гатовыя быць тыпізаваных, вы можаце разгледзець магчымасць выкарыстання HLists замест Спісы . Рэалізацыя ад бясформенныя бібліятэк ёсць метад ( фільтр ), які робіць тое, што ты хочаш. Прыклад </а >.
дададзена аўтар folone, крыніца
Калі вы гатовыя быць тыпізаваных, вы можаце разгледзець магчымасць выкарыстання HLists замест Спісы . Рэалізацыя ад бясформенныя бібліятэк ёсць метад ( фільтр ), які робіць тое, што ты хочаш. Прыклад </а >.
дададзена аўтар folone, крыніца

6 адказы

TL; DR

Выкарыстоўвайце гэта для Scala 2.9.x:

def typeOnly[T](seq : Seq[Any])(implicit m : Manifest[T]) : Seq[T] = { 
    seq.collect { 
        case t if m.erasure.isInstance(t) => t.asInstanceOf[T]
    }
}

scala> typeOnly[String](List(1,2,"3",4))
res1: Seq[String] = List(3)    

і гэта для Scala 2.10.x:

def typeOnly[T](seq : Seq[Any])(implicit tag : scala.reflect.ClassTag[T]) = { 
    seq.collect { 
        case t if tag.runtimeClass.isInstance(t) => t.asInstanceOf[T]
    }
}

Як Seq вызначаецца як рыса Seq [+ А] (ключ з'яўляецца + ), любы Seq [S], таксама Seq [Любы] .

З іншага боку, як ужо было сказана, Т «забыліся», калі функцыя кампілюецца, таму вы не можаце выкарыстоўваць яго непасрэдна. Вы павінны прайсці Класс T ў якасці параметру, як некаторыя.

def typeOnly[T](seq : Seq[Any], c : Class[T]) : Seq[T] = { 
    seq.flatMap { 
        case t if c.isInstance(t) => Some(t.asInstanceOf[T])
        case _ => None 
    }
} 

У Scala Appart ад Клас [Т] , ёсць таксама маніфесту [Т] , які з'яўляецца крыху больш магутным, і, як следства, больш ідыёматычны. У прыватнасці, ён мае метад сціраннем які вяртае Клас [Т] . Выкарыстоўваючы яго, вы маглі б пайсці і напісаць функцыю, як гэта:

def typeOnly[T](seq : Seq[Any], m : Manifest[T]) : Seq[T] = { 
    seq.flatMap { 
        case t if m.erasure.isInstance(t) => Some(t.asInstanceOf[T])
        case _ => None 
    }
} 

Здаецца, што мы не атрымалі нічога. Тым не менш, калі вы спытаеце кампілятар добра (выкарыстоўваючы няяўнай ), ён будзе перадаваць маніфесту для вас, калі вы выклікаеце функцыю.

def typeOnly[T](seq : Seq[Any])(implicit m : Manifest[T]) : Seq[T] = { 
    seq.flatMap { 
        case t if m.erasure.isInstance(t) => Some(t.asInstanceOf[T])
        case _ => None 
    }
}

прыклады:

scala> typeOnly[java.lang.Integer](List(1,2,"3",4))
res2: Seq[java.lang.Integer] = List(1, 2, 4)

scala> typeOnly[String](List(1,2,"3",4))
res3: Seq[String] = List(3)

scala> typeOnly[java.lang.Double](List(1,2,"3",4))
res4: Seq[java.lang.Double] = List()

Ёсць некалькі альтэрнатыў, некаторыя з іх яшчэ больш ідыёматычны. Можна, напрыклад, выкарыстоўваць збор з часткова пэўнай функцыі:

def typeOnly[T](seq : Seq[Any])(implicit m : Manifest[T]) : Seq[T] = { 
    seq.collect { 
        case t if m.erasure.isInstance(t) => t.asInstanceOf[T]
    }
}

WARNING: The former examples work in Scala 2.9.3 and below. If you are developing for Scala 2.10.x, Manifest#erasure has been deprecated. Use runtimeClass instead:

def typeOnly[T](seq : Seq[Any])(implicit m : Manifest[T]) = { 
    seq.collect { 
        case t if m.runtimeClass.isInstance(t) => t.asInstanceOf[T]
    }
}

Як выяўляецца хутка састарэе таксама (гл каментары ніжэй), вы павінны разгледзець магчымасць выкарыстання ClassTag.

def typeOnly[T](seq : Seq[Any])(implicit tag : scala.reflect.ClassTag[T]) = { 
    seq.collect { 
        case t if tag.runtimeClass.isInstance(t) => t.asInstanceOf[T]
    }
}
5
дададзена
Звярніце ўвагу, што ў Scala 2.10, варта аддаваць перавагу TypeTag над Manifest . <�Код> маніфесту плануецца састарэе. docs.scala-lang.org/overviews/reflection/…
дададзена аўтар gzm0, крыніца
Карысна ведаць. Фіксаваны яго.
дададзена аўтар vlopez, крыніца

TL; DR

Выкарыстоўвайце гэта для Scala 2.9.x:

def typeOnly[T](seq : Seq[Any])(implicit m : Manifest[T]) : Seq[T] = { 
    seq.collect { 
        case t if m.erasure.isInstance(t) => t.asInstanceOf[T]
    }
}

scala> typeOnly[String](List(1,2,"3",4))
res1: Seq[String] = List(3)    

і гэта для Scala 2.10.x:

def typeOnly[T](seq : Seq[Any])(implicit tag : scala.reflect.ClassTag[T]) = { 
    seq.collect { 
        case t if tag.runtimeClass.isInstance(t) => t.asInstanceOf[T]
    }
}

Як Seq вызначаецца як рыса Seq [+ А] (ключ з'яўляецца + ), любы Seq [S], таксама Seq [Любы] .

З іншага боку, як ужо было сказана, Т «забыліся», калі функцыя кампілюецца, таму вы не можаце выкарыстоўваць яго непасрэдна. Вы павінны прайсці Класс T ў якасці параметру, як некаторыя.

def typeOnly[T](seq : Seq[Any], c : Class[T]) : Seq[T] = { 
    seq.flatMap { 
        case t if c.isInstance(t) => Some(t.asInstanceOf[T])
        case _ => None 
    }
} 

У Scala Appart ад Клас [Т] , ёсць таксама маніфесту [Т] , які з'яўляецца крыху больш магутным, і, як следства, больш ідыёматычны. У прыватнасці, ён мае метад сціраннем які вяртае Клас [Т] . Выкарыстоўваючы яго, вы маглі б пайсці і напісаць функцыю, як гэта:

def typeOnly[T](seq : Seq[Any], m : Manifest[T]) : Seq[T] = { 
    seq.flatMap { 
        case t if m.erasure.isInstance(t) => Some(t.asInstanceOf[T])
        case _ => None 
    }
} 

Здаецца, што мы не атрымалі нічога. Тым не менш, калі вы спытаеце кампілятар добра (выкарыстоўваючы няяўнай ), ён будзе перадаваць маніфесту для вас, калі вы выклікаеце функцыю.

def typeOnly[T](seq : Seq[Any])(implicit m : Manifest[T]) : Seq[T] = { 
    seq.flatMap { 
        case t if m.erasure.isInstance(t) => Some(t.asInstanceOf[T])
        case _ => None 
    }
}

прыклады:

scala> typeOnly[java.lang.Integer](List(1,2,"3",4))
res2: Seq[java.lang.Integer] = List(1, 2, 4)

scala> typeOnly[String](List(1,2,"3",4))
res3: Seq[String] = List(3)

scala> typeOnly[java.lang.Double](List(1,2,"3",4))
res4: Seq[java.lang.Double] = List()

Ёсць некалькі альтэрнатыў, некаторыя з іх яшчэ больш ідыёматычны. Можна, напрыклад, выкарыстоўваць збор з часткова пэўнай функцыі:

def typeOnly[T](seq : Seq[Any])(implicit m : Manifest[T]) : Seq[T] = { 
    seq.collect { 
        case t if m.erasure.isInstance(t) => t.asInstanceOf[T]
    }
}

WARNING: The former examples work in Scala 2.9.3 and below. If you are developing for Scala 2.10.x, Manifest#erasure has been deprecated. Use runtimeClass instead:

def typeOnly[T](seq : Seq[Any])(implicit m : Manifest[T]) = { 
    seq.collect { 
        case t if m.runtimeClass.isInstance(t) => t.asInstanceOf[T]
    }
}

Як выяўляецца хутка састарэе таксама (гл каментары ніжэй), вы павінны разгледзець магчымасць выкарыстання ClassTag.

def typeOnly[T](seq : Seq[Any])(implicit tag : scala.reflect.ClassTag[T]) = { 
    seq.collect { 
        case t if tag.runtimeClass.isInstance(t) => t.asInstanceOf[T]
    }
}
5
дададзена
Звярніце ўвагу, што ў Scala 2.10, варта аддаваць перавагу TypeTag над Manifest . <�Код> маніфесту плануецца састарэе. docs.scala-lang.org/overviews/reflection/…
дададзена аўтар gzm0, крыніца
Карысна ведаць. Фіксаваны яго.
дададзена аўтар vlopez, крыніца

Your case t: T => Some(t) does not filter the elements by type T. What you need is something like this:

  def typeOnly[T](seq: Seq[Any], clazz: Class[T] = classOf[Any]): Seq[T] = {
    seq.flatMap{
      case t: T if clazz.isInstance(t) => Some(t)
      case _ => None
    }
  }

Што вы заклікаеце, як гэта:

typeOnly(Seq(1, "2", 3))
typeOnly(Seq(1, "2", 3), classOf[String])
2
дададзена

Your case t: T => Some(t) does not filter the elements by type T. What you need is something like this:

  def typeOnly[T](seq: Seq[Any], clazz: Class[T] = classOf[Any]): Seq[T] = {
    seq.flatMap{
      case t: T if clazz.isInstance(t) => Some(t)
      case _ => None
    }
  }

Што вы заклікаеце, як гэта:

typeOnly(Seq(1, "2", 3))
typeOnly(Seq(1, "2", 3), classOf[String])
2
дададзена

Калі вы імкнецеся на дазвол typeOnly карыстальнікам толькі пазначыць T параметр тыпу, можна вызначыць так:

def typeOnly[S](seq: Seq[S]) = new {
  def apply[T]: Seq[T] =
    seq.flatMap{
      case t: T => Some(t)
      case _ => None
    }
}

Вы б назваць гэта такім чынам,

val l = typeOnly(List(1, "2", 3, "4"))[String]

(Звярніце ўвагу на параметр тыпу пасля таго, як аргумент, а не толькі пасля typeOnly ).

Гэта дадае папярэджанне кампілятара з-за яго канкрэтнага прымянення структурных тыпаў. Калі вы хочаце, каб пазбегнуць дадання імпарт language.reflectiveCalls , каб выправіць гэта, вы можаце разлічваць на прамежкавым класе:

case class TypeOnlyFilter[S](seq: Seq[S]) {
  def apply[T]: Seq[T] =
    seq.flatMap{
      case t: T => Some(t)
      case _ => None
    }
}

def typeOnly[S](seq: Seq[S]) =
  new TypeOnlyFilter(seq)

Звярніце ўвагу, што гэтая рэалізацыя ўсё яшчэ мае кампілятар папярэджанне аб бескантрольным шаблоне абстрактнага тыпу T (як арыгінал рэалізацыя робіць). Вы проста павінны патрабаваць ClassTag для тыпу T , каб выправіць гэта, так што канчатковае рашэнне выглядае наступным чынам:

import scala.reflect.ClassTag

case class TypeOnlyFilter[S](seq: Seq[S]) {
  def apply[T: ClassTag]: Seq[T] =
    seq.flatMap{
      case t: T => Some(t)
      case _ => None
    }
}

def typeOnly[S](seq: Seq[S]) =
  new TypeOnlyFilter(seq)
0
дададзена

Калі вы імкнецеся на дазвол typeOnly карыстальнікам толькі пазначыць T параметр тыпу, можна вызначыць так:

def typeOnly[S](seq: Seq[S]) = new {
  def apply[T]: Seq[T] =
    seq.flatMap{
      case t: T => Some(t)
      case _ => None
    }
}

Вы б назваць гэта такім чынам,

val l = typeOnly(List(1, "2", 3, "4"))[String]

(Звярніце ўвагу на параметр тыпу пасля таго, як аргумент, а не толькі пасля typeOnly ).

Гэта дадае папярэджанне кампілятара з-за яго канкрэтнага прымянення структурных тыпаў. Калі вы хочаце, каб пазбегнуць дадання імпарт language.reflectiveCalls , каб выправіць гэта, вы можаце разлічваць на прамежкавым класе:

case class TypeOnlyFilter[S](seq: Seq[S]) {
  def apply[T]: Seq[T] =
    seq.flatMap{
      case t: T => Some(t)
      case _ => None
    }
}

def typeOnly[S](seq: Seq[S]) =
  new TypeOnlyFilter(seq)

Звярніце ўвагу, што гэтая рэалізацыя ўсё яшчэ мае кампілятар папярэджанне аб бескантрольным шаблоне абстрактнага тыпу T (як арыгінал рэалізацыя робіць). Вы проста павінны патрабаваць ClassTag для тыпу T , каб выправіць гэта, так што канчатковае рашэнне выглядае наступным чынам:

import scala.reflect.ClassTag

case class TypeOnlyFilter[S](seq: Seq[S]) {
  def apply[T: ClassTag]: Seq[T] =
    seq.flatMap{
      case t: T => Some(t)
      case _ => None
    }
}

def typeOnly[S](seq: Seq[S]) =
  new TypeOnlyFilter(seq)
0
дададзена