I need to write a generic method to get all fields of an object and it's value, the class of this object may contains ClassTag, so we should find a way to get it as well, is any way good way ? the difficulty is we don't know the class ahead, it may contains ClassTag (zero to many), It may not.
For example,
class A(x : Int) {}
a = new A(1)
We should output x => 1
class B[T: ClassTag]() {}
b = new B[Float]()
We should output _$1 = Float
def fields(obj: AnyRef) = obj.getClass.getDeclaredFields.map(field => (field.getName, field.get(obj))
will give you an array of pairs of field names and corresponding values, which you can massage into the format you want. You can test for types and do something depending on whether you have a ClassTag or not.
But for your specific examples: neither x in A nor the ClassTag in B are fields, they are just constructor parameters which aren't stored anywhere in the instance. To change this, you can declare it as a val:
class A(private val x: Int)
class B[T]()(private val tag: ClassTag[T])
or make sure they are used somewhere in the body outside the constructor.
Related
I'm confused about the generic type. I expect that 2.asInstanceOf[A] is cast to the type A, meanwhile, it's cast to Int.
Besides that, the input is java.lang.Long whereas the output is a list of Int (according to the definition the input and the output should be the same type). Why is that?
def whatever[A](x: A): List[A] = {
val two = 2.asInstanceOf[A]
val l = List(1.asInstanceOf[A],2.asInstanceOf[A])
println(f"Input type inside the function for 15L: ${x.getClass}")
println(f"The class of two: ${two.getClass}, the value of two: $two")
println(f"The class of the first element of l: ${l.head.getClass}, first element value: ${l.head}")
l
}
println(f"Returned from whatever function: ${whatever(15L)}")
the outupt:
Input type inside the function for 15L: class java.lang.Long
The class of two: class java.lang.Integer, the value of two: 2
The class of the first element of l: class java.lang.Integer, first element value: 1
Returned from whatever function: List(1, 2)
a.asInstanceOf[B] means:
Dear compiler;
Please forget what you think the type of a is. I know better. I know that if a isn't actually type B then my program could blow up, but I'm really very smart and that's not going to happen.
Sincerely yours, Super Programmer
In other words val b:B = a.asInstanceOf[B] won't create a new variable of type B, it will create a new variable that will be treated as if it were type B. If the actual underlying type of a is compatible with type B then everything is fine. If a's real type is incompatible with B then things blow up.
Type erasure. For the purposes of type checking 2 is cast to A; but at a later compilation stage A is erased to Object, so your code becomes equivalent to
def whatever(x: Object): List[Object] = {
val two = 2.asInstanceOf[Object]
val l = List(1.asInstanceOf[Object],2.asInstanceOf[Object])
println(f"Input type inside the function for 15L: ${x.getClass}")
println(f"The class of two: ${two.getClass}, the value of two: $two")
println(f"The class of the first element of l: ${l.head.getClass}, first element value: ${l.head}")
l
}
2.asInstanceOf[Object] is a boxing operation returning a java.lang.Integer.
If you try to actually use the return value as a List[Long] you'll eventually get a ClassCastException, e.g.
val list = whatever(15L)
val x = list(0)
x will be inferred to be Long and a cast inserted to unbox the expected java.lang.Long.
The answer from #jwvh is on point. Here I'll only add a solution in case you want to fix the problem of safely converting an Int to an A in whatever, without knowing what A is. This is of course only possible if you provide a way to build a particular A from an Int. We can do this in using a type-class:
trait BuildableFromInt[+A] {
def fromInt(i: Int): A
}
Now you only have to implicitly provide BuildableFromInt for any type A you wish to use in whatever:
object BuildableFromInt {
implicit val longFromInt: BuildableFromInt[Long] = Long.box(_)
}
and now define whatever to only accept compliant types A:
def whatever[A : BuildableFromInt](x: A): List[A] = {
val two = implicitly[BuildableFromInt[A]].fromInt(2)
// Use two like any other "A"
// ...
}
Now whatever can be used with any type for which a BuildableFromInt is available.
On a recent worksheet I was presented with the question asking what would be the output of the following code:
class A { def m(x:Double) = x+x }
class B[Any] extends A{ def m(x: Any) = print(x) }
class C[Any] { def m (x:Double) = x+x; def m (x: Any) = print(x) }
val obj1 = new B[Int]; val obj2 = new C[Any]
obj1.m(1); obj1.m(2.3); obj2.m(4); obj2.m(5.6)
I'm quite confused as to what having a concrete type in the square brackets after the class name would mean (i.e. class B[Any]). Is the later expression val obj1 = new B[Int] valid because Int <: Any, Int being a subclass of Any?
When later running the code snippet, the result given was simply "1" being printed. This was not what I had expected the call to obj.m(2.3) to resolve at def m(x: any), where it seems in actuality the compiler went up to A and called the m in class A.
The later expressions, obj2.m(4) and obj2.m(5.6) seems to make sense as both 4 and 5.6 would land in the function with def m(x: Double), thus not print anything out.
In what order exactly does the compiler traverse to find what to call? I'd be very grateful if someone could clear up my confusions with how polymorphism is handled here by Scala, thank you very much :)
When you do class B[Any], you define a class with a type parameter called Any. Don't confuse the type parameter name with the actual class Any. You are just shadowing its name.
You could just as fine do this:
class B[Int]
val obj = new B[String]
You may see why it is bad practice to name type parameters after actual types. Usually, people use single letter names for their type parameters, like this:
class B[T] // I just changed the name of the type parameter from "Int" to "T".
val obj = new B[String]
I'm trying to use discriminators in existing project and something is wrong with my classes I guess.
Consider this scodec example. If I change TurnLeft and its codec to
sealed class TurnLeft(degrees: Int) extends Command {
def getDegrees: Int = degrees
}
implicit val leftCodec: Codec[TurnLeft] = uint8or16.xmap[TurnLeft](v => new TurnLeft(v), _.getDegrees)
I get
Error:(x, x) could not find Lazy implicit value of type scodec.Codec[Command]
val codec: Codec[Either[UnrecognizedCommand, Command]] = discriminatorFallback(unrecognizedCodec, Codec[Command])
It all works if I make degrees field value field. I suspect it's something tricky with shapeless. What should I do to make it work ?
Sample project that demonstrates the issue is here.
shapeless's Generic is defined for "case-class-like" types. To a first approximation, a case-class-like type is one whose values can be deconstructed to it's constructor parameters which can then be used to reconstruct an equal value, ie.
case class Foo ...
val foo = Foo(...)
val fooGen = Generic[Foo]
assert(fooGen.from(fooGen.to(foo)) == foo)
Case classes with a single constructor parameter list meet this criterion, whereas classes which don't have public (lazy) vals for their constructor parameters, or a companion with a matching apply/unapply, do not.
The implementation of Generic is fairly permissive, and will treat (lazy) val members which correspond to constructor parameters (by type and order) as being equivalent to accessible constructor arguments, so the closest to your example that we can get would be something like this,
sealed class TurnLeft(degrees: Int) extends Command {
val getDegrees: Int = degrees
}
scala> Generic[TurnLeft]
res0: shapeless.Generic[TurnLeft]{type Repr = Int :: HNil } = ...
In this case getDegrees is treated as the accessor for the single Int constructor parameter.
Forgive me if the solution to this problem is too obvious or has been resolved already in this forum earlier (in which case, please point me to the post).
I have a class
org.personal.exercises.LengthContentsPair (l: Int, c: String)
{
val length = l
val contents = c
}
Then, in the same source file, I also define an implicit value which defines the way objects
of this type is to be ordered, thus:
object LengthContentsPair {
implicit val lengthContentsPairOrdering = new Ordering [LengthContentsPair] {
def compare (a: LengthContentsPair, b: LengthContentsPair)= {
a.length compare b.length;
}
}
}
following solutions given in this forum.
Now, I want to create a specialized Set which limits the number of elements in the Set to a given number. So, I define a separate class like this:
import scala.collection.immutable.TreeSet;
import org.personal.exercises.LengthContentsPair.lengthContentsPairOrdering;
class FixedSizedSortedSet [LengthContentsPair] extends TreeSet [LengthContentsPair]
{ ..
}
To me, this seems the correct way to subclass a TreeSet. But, the compiler throws the following error:
(1) No implicit Ordering defined for LengthContentsPair.
(2) not enough arguments for constructor TreeSet: (implicit ordering: Ordering[LengthContentsPair])scala.collection.immutable.TreeSet[LengthContentsPair]. Unspecified value parameter ordering.
Have I understood the scoping rules wrongly? It is something quite easy I feel, but I cannot put my hand on it.
You have defined FixedSizedSortedSet wrong. Your implementation has generic type parameter named LengthContentsPair which has nothing to do with your class with that name. In other words, you have shadowed LengthContentsPair class with generic type.
If you need a specialized set that only holds elements of LengthContentsPair, then you probably meant:
class FixedSizedSortedSet extends TreeSet[LengthContentsPair]
{ ..
}
This should work if an instance of Ordering[LengthContentsPair] is visible. But this shouldn't be a problem, since the ordering is defined in companion object of LengthContentsPair and is visible as implicit parameter by default.
But if you rather need a generic extension of TreeSet which can hold elements of any type, then you probably meant this:
class FixedSizedSortedSet[T](implicit ordering: Ordering[T]) extends TreeSet[T]
{ ..
}
Implicit parameter is needed because TreeSet requires an implicit Ordering[T], so we need to forward that requirement to FixedSizedSortedSet
BTW. I'd suggest you to consider replacing your LengthContentsPair class with a case class.
I'm moving my first steps in Scala and I would like to make the following code works:
trait Gene[+T] {
val gene: Array[T]
}
The error that the compiler gives is: covariant type T occurs in invariant position in type => Array[T] of value gene
I know I could do something like:
trait Gene[+T] {
def gene[U >: T]: Array[U]
}
but this doesn't solve the problem because I need a value: pratically what I'm trying to say is "I don't care of the inside type, I know that genes will have a gene field that return its content". (the +T here is because I wanna do something like type Genome = Array[Gene[Any]] and then use it as a wrapper against the single gene classes so I can have a heterogeneous array type)
Is it possible to do it in Scala or I'm simply taking a wrong approach? Would it be better to use a different structure, like a Scala native covariant class?
Thanks in advance!
P.S.: I've also tried with class and abstract class instead than trait but always same results!
EDIT: with kind suggestion by Didier Dupont I came to this code:
package object ga {
class Gene[+T](val gene: Vector[T]){
def apply(idx: Int) = gene(idx)
override def toString() = gene.toString
}
implicit def toGene[T](a: Vector[T]) = new Gene(a)
type Genome = Array[Gene[Any]]
}
package test
import ga._
object Test {
def main(args: Array[String]) {
val g = Vector(1, 3, 4)
val g2 = Vector("a", "b")
val genome1: Genome = Array(g, g2)
println("Genome")
for(gene <- genome1) println(gene.gene)
}
}
So I now think I can put and retrieve data in different types and use them with all type checking goodies!
Array is invariant because you can write in it.
Suppose you do
val typed = new Gene[String]
val untyped : Gene[Any] = typed // covariance would allow that
untyped.gene(0) = new Date(...)
this would crash (the array in your instance is an Array[String] and will not accept a Date). Which is why the compiler prevents that.
From there, it depends very much on what you intend to do with Gene. You could use a covariant type instead of Array (you may consider Vector), but that will prevent user to mutate the content, if this was what you intended. You may also have an Array inside the class, provided it is decladed private [this] (which will make it quite hard to mutate the content too). If you want the client to be allowed to mutate the content of a Gene, it will probably not be possible to make Gene covariant.
The type of gene needs to be covariant in its type parameter. For that to be possible, you have to choose an immutable data structure, for example list. But you can use any data structure from the scala.collection.immutable package.