Reflection: Cast a reflected class to its type and return it within a method

#1

Im looking to a have a method like this, with the this signature:

def reflectTheClassAndReturnIt (classPath: String = "Question"): Question = ???

Two things here:

  1. Loading the class and cast it to its type.
  2. Also reflecting the return type as well based on the name of the class.

Is it possible to do the above at all; in the Scala reflection API? I don’t know if my thinking is wrong and/or we have a limitation on using the reflection API. Any suggestion would be appreciated.

#2

Types only exist at compile time, so you cant have a return type (a compile time construct) depend the runtime value of a string.

You can do something with macros, but only for literal strings for which the value is known at compile time.

#3

Thanks Martin.
So lets say if I have a class Class InstantiateMeThroughReflection(){} can I use a string to reflectively instantiate this class during the run-time?

#4

Hmm – can you give a little more detail about what you’re trying to accomplish here? In particular, what do you mean by “reflectively instantiate”? It’s pretty unusual to create instances through reflection (for good reason), and folks asking questions like this are sometimes trying to do the impossible, but it might be that you’re asking for something reasonable. I suspect that you’re looking to do something that is better managed in other ways, but more details would be helpful…

#5

**scala> class Whatever
defined class Whatever

val myString = (new Whatever).getClass.getClassLoader.loadClass(“java.lang.String”).getConstructors.filter(_.getParameterCount == 0).head.newInstance().asInstanceOf[String]
myString: String = “”**

#6

It is about loading an instance of a class using a String. For example if I have “the.path.to.theclass.ClassName” I should be able to get the string and create and instance of the ClassName. The reason behind that is, I want to create a program which is expandable by the user. Where the user can add his/her classes to the program and uses it. For that; when Im creating my program I dont know what they are using it for; I want them to be able to configure it via set of Strings (class paths) and then the program instantiate that. This way I dont need to expand the codebase. The program instantiate and use the class or throws and error.

#7

So this can sort of be done, using reflection – but keep in mind, it’s inherently anti-strong-types. You can build a function that will do this (as shown by @curoli), but that function will have to return the broadest type encompassing all of the possible types that might be loaded this way – in the general case, it would have to return Any (or maybe AnyRef). This means that you can’t program it in normal Scala ways, since the compiler literally has no idea what types it is working with.

Basically, the “cast it to its type” part is impossible in the model you’re describing. Types only exist at compile time, and you’re saying you don’t want to know anything about this class until runtime. So the result is very un-Scala in a lot of ways. It’s the sort of operation that makes sense in a dynamically-typed language, but not so much in a strongly-typed one.

(It’s also wildly insecure, so should only be used with extreme caution, when you are absolutely sure that you totally trust all users.)

1 Like
#8

The code with the class loader above gives you what you’ve asked for. Unfortunately not what you want. The jvm datatypes (classes, methods) are like OS level subroutine calls. They are required for the jvm to operate. That’s their design.

Unfortunately, it isn’t designed for consumer orchestration. The only time I’ve used it in production is to be able to load runtime classes that implement a sub-system interface. The consumer calls the public interface. Then, based on a rule, will load a specific implementation of the sub-system interfaces. Probably the most obvious use was to load different localizations of the subsystem based on localization rules or a different implementation for a MassPar computer than a mainframe.

In both cases, we could have included all of the implementations in a single implementation jar. But that jar would be massive and unwieldy. We couldn’t add new functions while the system was running. We’d have to stop the system and restart it with the new code.

In those cases, the consumer’s don’t change their code. The subsystem interface just hides the implementation. What you’re asking for is a tremendously finer grain.

Also scala isn’t java. It just compiles down to the jvm. No different than compiling to machine code (other than faster). The data structures and execution context don’t reflect your scala code. One can run Fortran codes on the same jvm. The execution support scala and Fortran. Reverse execution would be equally confusing for each language. The original compilers would also compile down to the .Net environment.

It can be done, but it isn’t what you want.

#9

Can your user only choose from classes already in the app, or create their own classes, too? After a user-chosen class is instantiated, what will be done with the instance?

#10

They can create their own classes as well. The instantiated class then will be used within the application itself.

#11

Basically, sounds like you want an Interpreter. Scala has one.

#12

Let’s dig a little further – how will the instantiated class be used? Remember, this is a newly-created class, that the application doesn’t know about. How will it be passed to functions? How can those functions call methods on the user-defined class?

I suspect that, for this to be usable in any sensible way, these user-created classes need to conform to some sort of interface – that is, an application-defined trait. If so, that’s probably the answer to your original question: they need to implement that trait, the application uses the classloader to load them in, and then it casts the loaded class to that known trait.

But seriously: it’s worth thinking through your use case very carefully. Loading the user-defined class is the easy part – defining everything in such a way that you can actually use it is where you need some real design here.

(And as previously mentioned, this is all incredibly dangerous and prone to security problems, so do it only with great caution…)