Reputation: 13872
See: Can I specify a meaningful name for an anonymous class in C#?
In C# you can write:
var e = new { ID = 5, Name= "Prashant" };
assertEquals( 5, e.ID )
But in Scala I end up writing:
var e = (5, "Prashant")
assertEquals( 5, e._1 )
Scala maintains type safety through the use of generics (as does C#), but loses the readability of the name of each field, e.g I use "_1" instead of "ID".
Is there anything like this in Scala?
Upvotes: 22
Views: 22485
Reputation: 2216
As Juh_ suggests, extending a case class should do what you want:
scala> case class E(id: Int, name: String)
defined class E
scala> val e = new E(5, "Prashant")
e: E = E(5,Prashant)
scala> e.name
res3: String = Prashant
Case classes provide an equals method, and they also extend the Product
trait, which is the same trait that Scala tuples extend. Maybe in the future they will also extend the ProductN
traits.
If you use anonymous classes as suggested in some other answers, you don't end up with real tuples! For example, you don't get the equals method:
scala> val x = new { val count = 5 }
x: AnyRef{val count: Int} = $anon$1@29ca901e
scala> val y = new { val count = 5 }
y: AnyRef{val count: Int} = $anon$1@1dfe2924
scala> x == y
res4: Boolean = false
As of Scala 2.11 extending Tuple2
does work, but this is deprecated because you're not supposed to extend case classes.
You could also extend the Product2
trait, but that doesn't provide implementations of any methods, so you'd have to write all the methods yourself.
You could probably also use a Shapeless HList
, which would give you lots of fancy features at the cost of adding an external dependency.
I also tried Twitter's jaqen library, but that didn't compile for me in Scala 2.11.
I'm currently using Scala 2.11, so I can't guarantee that this advice applies to other versions of Scala.
Upvotes: 4
Reputation: 15529
I would simply make a case class:
object Yo{
case class E(id: Int, name: String)
def main(){
val e = E(5,"Prashant")
println("name="+e.name+", id="+e.id)
}
}
Not sure if it is as efficient as Daniel's answer but I expect it is the same (I'd appreciate comments on that). In any case, I find it more readable, using only one additional line that is shared if you have more than one instance if E
. Also you could add method to the case class, such as:
case class E(id: Int, name: String){
def hasId(id: Int) = this.id==id
}
val e = E(5,"Prashant")
assert(e hasId 5)
Upvotes: 9
Reputation: 26486
Scala 2.8 has improved the type system to the point that it is possible to have statically and heterogeneously typed arrays and lists, so presumably the same could be done with maps. Check out Jesper Nordenberg's blog on "Type Lists and Heterogeneously Typed Arrays" for his implementation.
Upvotes: 1
Reputation: 556
You can also name the parts of the tuple you're assigning to, as in:
val (ID, Name) = (5, "Prashant")
assertEquals( 5, ID )
You can also use this like:
val (ID, Name, Age) = functionThatReturnsATuple3
println("ID: " + ID + ", age: " + Age)
When I first read about the _x
syntax I thought it was great and used it a lot. I've since basically stopped using it as when I have to look at code I wrote two months ago I have to spend a load of time trying to work out what the types of the _1
, _2
etc. are. I suppose it's obvious in hindsight that id
is much more readable than pair._1
.
This can also be used inside functions like map
, filter
etc. like:
val list: List[ (Int, String, Double) ] = ...
list map { case (id, name, time) => ... }
list filter { case (_, name, _) => name == "X" }
Note that in the filter
you can use _
s for elements which you aren't going to use in the body of the function. This can be useful when skimming over code like that to establish what parts of structures are being used and how values are built up.
Upvotes: 20
Reputation: 297155
object T {
def main(args: Array[String]) {
val e = new { var id = 5; var name = "Prashant" }
assert(e.id == 5)
}
}
Ok, let's make stuff clear. This does use reflection on Scala 2.7 and Scala 2.8, because the type of e
is, in this case, a structural type, which Scala handles through reflection. Here is the generated code, at clean-up time (scalac -Xprint:cleanup
):
package <empty> {
final class T extends java.lang.Object with ScalaObject {
private <synthetic> <static> var reflMethod$Cache1: java.lang.reflect.Method = null;
private <synthetic> <static> var reflClass$Cache1: java.lang.Class = null;
<synthetic> <static> def reflMethod$Method1(x$1: java.lang.Class): java.lang.reflect.Method = {
if (T.this.reflMethod$Cache1.eq(null).||(T.this.reflClass$Cache1.ne(x$1)))
{
T.this.reflMethod$Cache1 = x$1.getMethod("id", Array[java.lang.Class]{});
T.this.reflClass$Cache1 = x$1;
()
};
T.this.reflMethod$Cache1
};
@remote def $tag(): Int = scala.ScalaObject$class.$tag(T.this);
def main(args: Array[java.lang.String]): Unit = {
val e: java.lang.Object = {
new T$$anon$1()
};
scala.this.Predef.assert(scala.Int.unbox({
var exceptionResult1: java.lang.Object = _;
try {
exceptionResult1 = T.reflMethod$Method1(e.getClass()).invoke(e, Array[java.lang.Object]{})
} catch {
case ($1$ @ (_: java.lang.reflect.InvocationTargetException)) => {
exceptionResult1 = throw $1$.getCause()
}
};
exceptionResult1
}.$asInstanceOf[java.lang.Integer]()).==(5))
};
def this(): object T = {
T.super.this();
()
}
};
final class T$$anon$1 extends java.lang.Object {
private[this] var id: Int = _;
<accessor> def id(): Int = T$$anon$1.this.id;
<accessor> def id_=(x$1: Int): Unit = T$$anon$1.this.id = x$1;
private[this] var name: java.lang.String = _;
<accessor> def name(): java.lang.String = T$$anon$1.this.name;
<accessor> def name_=(x$1: java.lang.String): Unit = T$$anon$1.this.name = x$1;
def this(): T$$anon$1 = {
T$$anon$1.this.id = 5;
T$$anon$1.this.name = "Prashant";
T$$anon$1.super.this();
()
}
}
}
There is some caching going on, but if I alternated between id
and name
it would invalidate the cache already. Scala 2.8 also does reflection, and also caches, but it uses a more efficient caching technique, which should provide better overall performance. For reference, here is the clean-up of Scala 2.8:
package <empty> {
final class T extends java.lang.Object with ScalaObject {
final private <synthetic> <static> var reflParams$Cache1: Array[java.lang.Class] = Array[java.lang.Class]{};
@volatile
private <synthetic> <static> var reflPoly$Cache1: scala.runtime.MethodCache = new scala.runtime.EmptyMethodCache();
<synthetic> <static> def reflMethod$Method1(x$1: java.lang.Class): java.lang.reflect.Method = {
var method1: java.lang.reflect.Method = T.reflPoly$Cache1.find(x$1);
if (method1.ne(null))
return method1
else
{
method1 = x$1.getMethod("id", T.reflParams$Cache1);
T.reflPoly$Cache1 = T.reflPoly$Cache1.add(x$1, method1);
return method1
}
};
def main(args: Array[java.lang.String]): Unit = {
val e: java.lang.Object = {
new T$$anon$1()
};
scala.this.Predef.assert(scala.Int.unbox({
val qual1: java.lang.Object = e;
{
var exceptionResult1: java.lang.Object = _;
try {
exceptionResult1 = T.reflMethod$Method1(qual1.getClass()).invoke(qual1, Array[java.lang.Object]{})
} catch {
case ($1$ @ (_: java.lang.reflect.InvocationTargetException)) => {
exceptionResult1 = throw $1$.getCause()
}
};
exceptionResult1
}.$asInstanceOf[java.lang.Integer]()
}).==(5))
};
def this(): object T = {
T.reflParams$Cache1 = Array[java.lang.Class]{};
T.reflPoly$Cache1 = new scala.runtime.EmptyMethodCache();
T.super.this();
()
}
};
final class T$$anon$1 extends java.lang.Object {
private[this] var id: Int = _;
<accessor> def id(): Int = T$$anon$1.this.id;
<accessor> def id_=(x$1: Int): Unit = T$$anon$1.this.id = x$1;
private[this] var name: java.lang.String = _;
<accessor> def name(): java.lang.String = T$$anon$1.this.name;
<accessor> def name_=(x$1: java.lang.String): Unit = T$$anon$1.this.name = x$1;
def this(): T$$anon$1 = {
T$$anon$1.super.this();
T$$anon$1.this.id = 5;
T$$anon$1.this.name = "Prashant";
()
}
}
}
Upvotes: 24