11

Java claims to be object oriented and typesafe, and Scala even more so.

Internal Class fields are represented by class called Field, which you can obtain a reference to via the Reflection API.

My question: do these languages provide any way to obtain that Field reference in a typesafe way? (And if not, why on earth not? Seems like a glaring deficiency)

It would be extremely useful when mapping an Object to some external representation, for example to html fields in a template, or to column names in a database, to keep the reference names automatically in sync.

Ideally I'd like to say something like:

&(SomeClass.someField).name() 

to get the name of the field declaration, similar to how java enums let you say:

MyEnum.SOME_INSTANCE.name()

[update:] after reading feedback that this functionality would somehow violate the intent of the Reflection API, I agree that Reflection is designed for things that aren't known at compile time, and that's exactly why it's so absurd to have to use it to learn things that are known at compile time, namely the Fields of the very class that it's compiling!

The compiler provides this for enums, so if the compiler is able to access the enum Field's reference to allow MyEnum.SOME_INSTANCE.name(), then there's no logical reason why it shouldn't also be able to provide this same functionality to ordinary Classes.

Is there any technological reason why this functionality couldn't be there for ordinary classes? I don't see why not, and I disagree that this functionality would "complicate" things... on the contrary it would vastly simplify the present cumbersome Reflection API techniques. Why force developers into Reflection to find out something that is known at compile time?

[update #2] as for the utility of this feature, have you ever tried using the Criteria API in JPA or Hibernate to dynamically construct a query? Have you seen the absurd work-arounds people have come up with to try to avoid having to pass in an unsafe String representation of the field to query against?

[update #3] Finally, a new JVM language called Ceylon has heeded the call and makes this trivial to do!

Magnus
  • 10,736
  • 5
  • 44
  • 57
  • I don't really understand your point. If `&(SomeClass.someField).name()` can be statically detected why not just write `SomeClass.someField.name()`? Is `SomeClass.someField` a String? If yes, how should the compiler recognize its value (it can be filled in at runtime)? – kiritsuku Mar 24 '12 at 23:36
  • The point is not to obtain the String's value, but to obtain the name that you've assigned to that String field itself. Eg if the field was defined as: public String title = "president"; then I want to call &(SomeClass.title).name() and get back "title" – Magnus Mar 25 '12 at 02:13
  • If you know the name why not just accessing it directly? You mentioned something with "column names in a database", but when you change the field you also have to change all references to it. And this can be done with modern IDEs without using the feature you want to have. – kiritsuku Mar 25 '12 at 11:23
  • After using annotation processors with jpa2 criteria, I do agree wholeheartedly with this feature. Android also came up with some weird workarounds to this limitation with stuff like `static String FIELD="field"`. I've had some success with this using [typechecked metaprogramming](https://github.com/will-lp/latitude) in Groovy. – Will Dec 16 '14 at 12:24

6 Answers6

6

It is a pity that Java still misses this feature. This feature would not add additional complexity because it would interfere with other aspects of the language. Furthermore, being a feature that would be rarely used is not excuse. Every language is full of features and most projects make use of a small subset of them.

I really don't understand why the language allows me to do this:

Field field = MyClass.class.getField("myField"); // verbose syntax, evaluate at runtime, not type-safe, must deal with Reflective operation exceptions

But it doesn't let me do (something like) this:

Field field = MyClass::myField; // compact syntax, evaluated at compile-time, type-safe, no exceptions!

(the "::" operator is just a suggestion, borrowed from java 8 or c++)

Giannis
  • 91
  • 1
  • 1
6

My question: do these languages provide any way to obtain that Field reference in a typesafe way?

Compile-time typesafe? Not that I'm aware of, at least in Java. The normal purpose of reflection in Java is for code to be able to deal with types it has no knowledge of before-hand - it's rare (in my experience) to be in a position where you want to be able to refer to a field in a known type. It does happen, but it's not very common.

(And if not, why on earth not? Seems like a glaring deficiency)

Every feature needs to be designed, implemented, tested, and has to meet the balance of providing more value than the added complexity in the language.

Personally I can think of features I'd much rather see in Java than this.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 2
    I would posit that the only reason it's rare to *want* it is because people haven't *seen* it. I think if this feature became available a whole class of programming patterns would arise to address at compile time what currently is tediously only addressed at runtime. – Magnus Mar 25 '12 at 15:25
  • 2
    @Magnus: I would count "currently being tediously addressed at runtime" as "wanting to be able to refer to a field in a known type". I'm not saying it *wouldn't* be useful - and I know the C# team has considered something similar (http://blogs.msdn.com/b/ericlippert/archive/2009/05/21/in-foof-we-trust-a-dialogue.aspx) but I still don't think it would be quite the world-transforming revelation you're implying. – Jon Skeet Mar 25 '12 at 15:30
  • 2
    Thanks for the link, very interesting discussion! My proposal however, targeting fields and not methods, avoids the "overloading ambiguity" pitfalls described, and the scoping question seems like a non-issue: just follow normal field scoping rules as always. Enlightening discussion nonetheless. – Magnus Mar 26 '12 at 21:10
3

In Scala you can you might use macros for it. See the following:

Example:

class Car(val carName: String);

object Main {
  def main(args: Array[String]): Unit = {
    println(FieldNameMacro.getFieldName[Car](_.carName))
  }
}

So this prints the field name "carName". If you rename the field "carName" to "cName" it would print "cName" instead.

Macro:

In this case actually the expression tree of "_.carName" is passed to the macro handler, rather a executable method. In our macro we can look into this expression tree and find out the name of the field that we are referring to.

import scala.reflect.macros.whitebox.Context
import scala.language.experimental.macros
import scala.reflect.runtime.universe._
import scala.reflect.ClassTag

object FieldNameMacro {
  def getFieldNameImpl[T](c: Context)(block: c.Expr[T => AnyRef]): c.Expr[String] = {
    import c.universe._
    // here we look inside the block-expression and 
    // ... bind the TermName "carName" to the value name
    val Expr(Function(_, Select(_, TermName(name: String)))) = block;
    // return the name as a literal expression
    c.Expr(Literal(Constant(name)));
    // Uncomment this to get an idea of what is "inside" the block-expression
    // c.Expr(Literal(Constant(showRaw(block))));
  }

  def getFieldName[T](block: (T) => AnyRef): String = macro getFieldNameImpl[T]
}

I took some inspiration from http://blogs.clariusconsulting.net/kzu/linq-beyond-queries-strong-typed-reflection/. The post is about the same issue but with respect to C#.


Shortcomings

Beware that the Macro has to be called exactly as above. As for instance the following usage will lead to a compiler exception (actually it is a Scala match exception within the macro).

object Main {
  def main(args: Array[String]): Unit = {
    val block = (car : Car) => car.carName;
    println(FieldNameMacro.getFieldName[Car](block))
  }
}

The problem is that a different expression tree is passed to the macro handler. For more details about this problem have a look at Scala Macro get value for term name

Community
  • 1
  • 1
3

In Scala 2.11 we can use this:

object R_ {
  def apply[T](x: (T) => AnyRef): (Class[T], Method) = macro impl
  def impl(c: whitebox.Context)(x: c.Tree) = { import c.universe._
    val q"((${_: TermName}:${a: Type}) => ${_: TermName}.${p: TermName})" = x

    val typeDef = a.typeSymbol
    val propertyDef = p.toString

    q"(classOf[$typeDef], classOf[$typeDef].getMethod($propertyDef))"
  }
}

Usage:

class User(val name: String)

object Test extends App {
  println(R_((a: User) => a.name))
}

And result will be:

(class mref.User,public java.lang.String mref.User.name())

jreznot
  • 2,694
  • 2
  • 35
  • 53
1

Why on earth not is because you don't know the type of the field at compile-time when using reflection. This is the whole point of reflection: to give you access to class information at runtime. Of course you'll get a runtime error if you use the wrong type, but that doesn't really help much.

Unfortunately, as tricky as it is to keep names the same, it's typically even trickier to keep types the same, so it's probably not worth it for the application you have in mind.

Right now there's no way to do what you want with reasonable effort in either Scala or Java. Scala could add this information to its manifests (or somewhere else), but it doesn't presently and it's not clear to me that it's worth the effort.

Rex Kerr
  • 166,841
  • 26
  • 322
  • 407
  • 4
    But we *do* know the type of the field at compile time, it's right there in the class definition! If reflection lets me get the Field reference via its String name, why not let me directly take the reference from the class definition? – Magnus Mar 24 '12 at 22:37
  • You do know the type but _not via reflection_. The compiler could sensibly make available a nameOf(myClass.x) method (either in Scala or Java), but it could _not_ sensibly make a getField(myClass,"x") method that was sensibly typed without dramatically different language features (i.e. `"x"` and `"date"` are both just strings, but on the basis of the _content_ of the string you would want the compiler to infer two different types...what would happen if that string was just `s`?). So, anyway, reflecting types at compile-time: no, not easy. Static conversion of field name to string: possible. – Rex Kerr Mar 24 '12 at 22:55
  • 5
    That's exactly my point -- that it would have been easy for language designers to provide this feature, but they simply didn't. In other words, it's an omission (or I would say "flaw") in the language. – Magnus Mar 24 '12 at 23:10
  • @Magnus: Probably the reason why this feature doesn't exist is that it is not as useful in a statically typed language as in a dynamically typed one. – kiritsuku Mar 24 '12 at 23:25
  • @Magnus - See Jon Skeet's answer. "Every feature needs to be designed, implemented...can think of features I'd much rather see...". It's a pretty minor feature. You can always write a little code generator if you think you're likely to mess it up if you do it by hand. – Rex Kerr Mar 24 '12 at 23:47
  • 4
    @Rex Kerr: I dont agree that it's minor. There's a whole class of meta-programming and integration tools like Hibernate/JPA, Spring, Aspect J, Adobe Flex that need to do something like this as part of their "magic". These tools would be less verbose and brittle if this feature were available, and I suspect many developer scenarios would no longer require magic tools at all. – Magnus Mar 25 '12 at 15:17
  • 1
    @Magnus - A lot of those tools absolutely require that it go the other way (i.e. lookup of text field of name in a statically compiled context is not enough). Still, you may be right that this feature would be good enough to remove some of the motivation for that tool set. – Rex Kerr Mar 25 '12 at 16:08
0

In Kotlin it looks like this:

SomeClass::someField.name

It is very close to your ideal:

&(SomeClass.someField).name()