How should I go about mapping a string to a class in Haxe, and then instantiating it?
class Foo{}
class Bar extends Foo{}
class Buzz extends Foo{}
// (...)
var classMap:Map<String, Class<Foo>> = [
"abc" => Bar,
"def" => Buzz
];
var myClass:Class<Foo> = classMap["abc"];
var myObj:Foo = new myClass(/* params */);
I thought this would work, but it throws unexpected ( after myClass. What's wrong?
Instead of storing Class<T> in the map and resorting to reflection for instantiation, it's a much nicer pattern to store references to the constructors (using their function type) as detailed in this haxe.org blog post.
class Foo {}
class Bar extends Foo {
public function new() {}
}
class Buzz extends Foo {
public function new() {}
}
var constructors:Map<String, Void->Foo> = [
"abc" => Bar.new,
"def" => Buzz.new
];
var construct:Void->Foo = constructors["abc"];
var myObj:Foo = construct();
https://try.haxe.org/#49E93
Unlike the Type.createInstance() approach, this doesn't allow you to pass arbitrary arguments to the constructors they might not even accept, so it's much more type-safe. It also automatically works with dead code elimination because Haxe sees that the constructors are referenced.
You should use Type.createInstance to create an instance of that class. new will only work if you want to directly call the constructor on the Class name itself new Foo()
I suggest you do it this way:
var myClass:Class<Foo> = classMap["abc"];
var myObj:Foo = Type.createInstance(myClass, [/* params */]);
Try it online here: https://try.haxe.org/#3134A
Related
I have a Controller with a constructor where I´m injecting a cache, but also I would like to invoke a method in the constructor when the instance is created. I know we can create some auxiliary constructors with
def this(foo:Foo){}
But in my case because is play framework the one that instance my bootstrap is a little bit more complex.
Here my code
class SteamController #Inject()(cache: CacheApi) extends BaseController {
private val GAME_IDS_LIST_API: String = "api.steampowered.com/ISteamApps/GetAppList/v2"
private val GAME_API: String = "store.steampowered.com/api/appdetails?appids="
private val GAME_KEY: String = "games"
def games = Action { implicit request =>
var fromRequest = request.getQueryString("from")
if (fromRequest.isEmpty) {
fromRequest = Option("0")
}
val from = Integer.parseInt(fromRequest.get) * 10
val to = from + 10
loadGameIds()
Ok(html.games(SteamStore.gamesIds(cache.getVal[JSONArray](GAME_KEY), from, to), cache.jsonArraySize(GAME_KEY)/10))
}
private def loadGameIds(): Unit = {
val games = cache.get(GAME_KEY)
if (games.isEmpty) {
get(s"$GAME_IDS_LIST_API", asJsonGamesId)
cache.set(GAME_KEY, lastResponse.get, 60.minutes)
}
}
What I would like is that loadGameIds would be invoked and cached when the class is instantiated.
Any suggestion?
Regards.
If I understand correctly your question, you simply want to add some statements to the main constructor body? If that's the case, you can simply do so in the body of the class itself. In your case, that would look like this:
class SteamController #Inject()(cache: CacheApi) extends BaseController {
...
private val GAME_KEY: String = "games"
loadGameIds() // <-- Here we are calling from the main constructor body
def games = Action { implicit request =>
...
}
...
}
When doing so, it is usually a good idea to perform additional code after the declaration of all vals and vars in your class, to ensure they are properly initialized at the time your additional constructor code runs.
Could someone explain why scala would allow a public variable, to satisfy the implementation of an abstract declared Protected item? My first assumption is that the compiler would complain, but I created a small test to see if this worked, and to my surprise it does. Is there an advantage to this? (perhaps this is normal in OOP?) Any methods to avoid the accidental pitfall?
object NameConflict extends App {
abstract class A {
protected[this] var name:String
def speak = println(name)
}
class B(var name:String) extends A { //notice we've declared a public var
}
val t = new B("Tim")
t.speak
println(t.name) // name is exposed now?
}
It's normal and as in Java. Sometimes it's desirable to increase the visibility of a member.
You can't do it the other way around and turn down visibility in a subclass, because the member can by definition be accessed through the supertype.
If invoking a method has terrible consequences, keep the method private and use a template method that can be overridden; the default implementation would invoke the dangerous method.
abstract class A {
private[this] def dangerous = ???
final protected def process: Int = {
dangerous
template
}
protected def template: Int = ???
}
class B extends A {
override def template = 5
}
Consider such code (this is just example not real code):
class Foo(url : String) extends Bar(url)
{
def say() { println(url) }
}
It compiles and it works. With nonsense results "of course". I am too newbie to judge, but for me it serves no purpose but confusion -- by definition it is impossible that the argument of the constructor could be reachable directly in another method.
Could someone more experience in Scala could point out condition when it might work or make sense? Or confirm my suspicion this is flaw in current Scala compiler.
Update
class Bar(val Url : String)
{
}
Why "nonsense". Because my will (no "var" and no "val" in Foo) was to just pass the argument, nothing else. So when I actually use the constructor argument it is just matter of what entity I use by mistake. When you write on purpose, you hit the jackpot each time, but if you don't (i.e. you make a mistake in spelling), you use entities by random. It is not the code does not make sense, it is the computation just does not make sense, I could roll a dice as well. There is a method scope, and I don't see a reason why there shouldn't be constructor scope.
Update -- workaround
So it seems this evil construct is really part of the language (by design). As UserUnknown and Tomasz Nurkiewicz suggested the only line of defense against making stupid typo is convention, however lower-upper case is not good. "A" and "a" differ a lot, but "U" and "u" not much. Introducing one special character (as Tomasz showed) is much better, because it is possible to visually detect fishy usage of constructor argument.
I will use "$" for "just-passing" constructor arguments, it is harder to type for me, and you don't see this character too often in the code.
Thank you for the answers!
Why it is evil? Because implicit actions should be allowed explicitly by users -- good examples are "dynamic" in C#, implicit conversions in Scala. And examples of breaking this rule which led to tons of problems are -- implicit conversions to bool in C++, implicit constructors in C++, declaration by usage in Perl. And this particular case is very, very close to the mentioned perlism, in Perl finally there was change in interpreter to detect such misusages, why Scala repeated the same mistake? I wonder.
Your suspicions are entirely merit-less. This is by design.
Parameters of a class are part of the class. They'll be preserved as field if necessary (such as in your example), or not if they are never used outside construction.
So, basically, if you don't need it as a field, it won't be. If you do, it will. And you'll never write a single extra character of code to tell the compiler what it can figure out by itself.
It's not a bug, it's a feature. In fact, a really nice one. Need an example how useful it is? Here is how I use it with Spring and dependency injection via constructor:
#Service
class Foo #Autowired() (bar: Bar, jdbcOperations: JdbcOperations) {
def serverTime() = bar.format(jdbcOperations.queryForObject("SELECT now()", classOf[Date]))
}
Equivalent code in Java:
#Service
public class Foo
{
private final Bar bar;
private final JdbcOperations jdbcOperations;
#Autowired
public Foo(Bar bar, JdbcOperations jdbcOperations)
{
this.bar = bar;
this.jdbcOperations = jdbcOperations;
}
public String serverTime()
{
return this.bar.format(this.jdbcOperations.queryForObject("SELECT now()", Date.class));
}
}
Still not convinced?
Short tutorial:
class Foo(var x: Int, val y: Int, z: Int) {
println(z)
//def zz = z
}
x will become a variable with getters and setter. y will become an immutable variable and z will become an immutable variable only if zz method is uncommented. Otherwise it will remain a constructor argument. Neat!
UPDATE: I see your point now! The following code works as expected by accessing url variable in base class:
class Bar(val url)
class Foo(_url : String) extends Bar(_url)
{
def say() { println(url) }
}
I agree, this is both ugly and is asking for trouble. In fact I once hit this problem myself when using Scala classes as Hibernate entities - I used constructor parameter instead of field in base class which caused duplicated field to be created: one in base class and one in derived class. I wouldn't even notice but Hibernate was screaming at runtime that duplicated column mapping was defined.
So I have to somewhat agree with you - this is somehow confusing and might be error-prone. This is the price you pay for "implicitness" and concise code.
However note that no modified and val modifier before constructor argument are different. Without modified immutable field is created, while val additionally adds getter.
Scala creates a field from a constructor parameter when such an parameter is referenced by a method in the class. I'm having trouble finding fault with the way that this works.
For the simple case everything works as expected:
scala> class Bar(val url: String)
defined class Bar
scala> class Foo(url: String) extends Bar(url) {
| def say() { println(url) }
| }
defined class Foo
scala> new Foo("urlvalue").say
urlvalue
If we introduce some confusion over the case of the constructor parameter this example still works as expected:
scala> class Bar(val Url: String)
defined class Bar
scala> class Foo(url: String) extends Bar(url) {
| def say() { println(url) }
| }
defined class Foo
scala> new Foo("urlvalue").say
urlvalue
Interestingly you might think that this has worked because it has introduced a lower-case url field in Foo in addition to the upper case Url in Bar, but that doesn't seem to be the case - the compiler seems to be smart enough to know that it can go to Url to get the value of url in say, as no lower case field is generated.
scala> :javap -private Bar
Compiled from "<console>"
public class Bar extends java.lang.Object implements scala.ScalaObject{
private final java.lang.String Url;
public java.lang.String Url();
public Bar(java.lang.String);
}
scala> :javap -private Foo
Compiled from "<console>"
public class Foo extends Bar implements scala.ScalaObject{
public void say();
public Foo(java.lang.String);
}
The only time I can see that this gets confusing is if you mis-spell a var field. In this case you do actually introduce a new field, and the two can get out of step.
scala> class Bar(var Url: String)
defined class Bar
scala> class Foo(url: String) extends Bar(url) {
| def say() { println(url) }
| }
defined class Foo
scala> val f = new Foo("urlvalue")
f: Foo = Foo#64fb7efa
scala> f.say
urlvalue
scala> f.Url = "newvalue"
f.Url: String = newvalue
scala> f.say
urlvalue
scala> :javap -private Foo
Compiled from "<console>"
public class Foo extends Bar implements scala.ScalaObject{
private final java.lang.String url;
public void say();
public Foo(java.lang.String);
}
I have a scala class:
class Foo {
def this(st: String) {
var baz = List[String]()
var jaz = "one" + st
// more code logic
}
}
First above code does not compile. Secondly I want baz and jaz to be private variables local to the lone constructor above and not instance variables.
How to solve this problem?
It does not compile because the first thing that MUST happen in an auxiliary constructor is a call to the primary constructor. I don't understand the second question, since the variables you have declared already ARE private
Something like this is maybe what you are looking for?
class Foo(st: String) {
val myInstance = {
var baz = List[String]()
var jaz = "one" + st
jax + baz.mkString(":")
}
}
The body of your class is the constructor. If you want to have some temporary values, you can declare a block with just about anything you want; that block can return a value, and you can store that value in an instance variable, in this case myInstance.
I've been curious about the impact of not having an explicit primary constructor in Scala, just the contents of the class body.
In particular, I suspect that the private or protected constructor pattern, that is, controlling construction through the companion object or another class or object's methods might not have an obvious implementation.
Am I wrong? If so, how is it done?
You can declare the default constructor as private/protected by inserting the appropriate keyword between the class name and the parameter list, like this:
class Foo private () {
/* class body goes here... */
}
Aleksander's answer is correct, but Programming in Scala offers an additional alternative:
sealed trait Foo {
// interface
}
object Foo {
def apply(...): Foo = // public constructor
private class FooImpl(...) extends Foo { ... } // real class
}