Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

object.getClass is an error in annotation #2453

Closed
scabug opened this issue Oct 7, 2009 · 15 comments
Closed

object.getClass is an error in annotation #2453

scabug opened this issue Oct 7, 2009 · 15 comments
Assignees
Labels
annotations fixed in Scala 3 This issue does not exist in the Scala 3 compiler (https://github.com/lampepfl/dotty/) has PR
Milestone

Comments

@scabug
Copy link

scabug commented Oct 7, 2009

scala lib version: 2.8.0-SNAPSHOT (timestamp: 20091006.003811 buildNumber: 232)

I can't pass the class of an object to a (Java) annotation (for ex, this one: https://svn.apache.org/repos/asf/directory/apacheds/trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/annotations/Factory.java )

The Factory annotation expects a Factory implementation, which is just class with a static method "newInstance", and so is done in Scala with an object.

In Java, I write: @Factory( MyFactory.class )

In scala, @Factory( classOf[MyFactory] ) won't work as MyFactory is an object and @Factory( MyFactory.getClass ) doesn't compile due to the error:

"annotation argument needs to be a constant; found: MyFactory.getClass"

I don't see any other way to pass the object's class as the annotation parameter.

@scabug
Copy link
Author

scabug commented Oct 7, 2009

Imported From: https://issues.scala-lang.org/browse/SI-2453?orig=1
Reporter: Francois Armand (fanf)
See #6259

@scabug
Copy link
Author

scabug commented Oct 21, 2009

Paul LaCrosse (pjlacrosse) said:
I have a similar problem:

Using the Oracle Berkeley DB for Java, I get:

"annotation argument needs to be a constant; found: ONE_TO_ONE{}"

when using this annotation:

@SecondaryKey(relate = ONE_TO_ONE)

where "ONE_TO_ONE" was imported as:
import com.sleepycat.persist.model.Relationship.ONE_TO_ONE

Interestingly, this problem shows up only in the Eclipse plugin manifesting as the error icon against the line in the editor, but not under the problems views. It also does not generate an error from a command-line scalac compilation.

This is using the 2.8 nightly from 20091021.

@scabug
Copy link
Author

scabug commented Oct 21, 2009

Paul LaCrosse (pjlacrosse) said:
... ignore that question mark next to the SecondaryKey annotation... it is not part of the code and appears to be a part of the wiki formatting.

@scabug
Copy link
Author

scabug commented Nov 12, 2009

@lrytz said:
Hi Fanf,

the question is which Class object should be passed to the annotation. For top-level scala objects, the scala compiler generates two classfiles

  object o { def x = 1 }

you get

  • o.class, which has static methods forwarding to the methods of o$$, i.e.
  class o { static int x() { return o$$.MODULE$$.x(); } }
  • o$$.class, which contains the actual implementation of the object's methods. The static initializer creates an instance of o$$ and stores it in the MODULE$$ field
  class o$$ {
    o$$ MODULE$$ = new o$$;
    int x() { return 1; }
  }

I don't know what org.apache.directory.server.core.integ.annotations.Factory expects. If it wants static factory methods, we should pass the o class, not o$$.

However, calling o.getClass in scala returns

scala> o.getClass
res0: java.lang.Class[_] = class o$$

For nested objects, the additional classfile with static forwarders is not generated. Instead, a method is added to the containing class:

  class A { object p { def x = 1 } }

generates

  • A.class with
  class A {
    A$$p$$ p$$module = null;
    A$$p$$ p() {
      if (p$$module == null) { p$$module = new A$$p$$ };
      return A$$p$$;
    }
  }
  • A$$p$$.class with
  class A$$p$$ { int x() { return 1; } }

So in this case there doesn't exist any static methods which could be called.

Again, getClass returns the object A$$p$$:

scala> (new A).p.getClass
res0: java.lang.Class[_] = class A$$p$$

We cannot allow o.getClass to be a constant because it isn't. Maybe we could hack something because we know that objects are final.

I could allow classOf[o.type] and load the module class in this case (o$$), but I don't know if this helps in your case. I cannot load the class with the static forwarders (o) because this is only generated in the backend, and only for top-level objects.

@scabug
Copy link
Author

scabug commented Nov 12, 2009

Francois Armand (fanf) said:
Hum, that's much more complex that I thougth.
For my personnal use case, I just noticed that now, ApacheDS accept class and instance method for the Factory annotation, so on that regard, my problem is solved.

Regarding the general problem, without your explanations I would have say "load the class with the static forwarder", but it doesn't seems to be possible :)

I don't know what is the best second choice. Or perhaps, there is just not enought use cases to decide (as even mine isn't relevant anymore), and you could close the bug for now, waiting to have better example to know what to do.

Sorry for the time spend, and thanks for the explanations.

@scabug
Copy link
Author

scabug commented Aug 26, 2011

Hongxin Liang (honnix) said:
Sorry to bring this up again.

As an experiment, I tried to combine scala with powermock, but I met exactly the same problem described by this issue.

In order to mock a static class (object in scala), I need to write annotation @PrepareForTest(TheStaticClass.class), but this does not work as described.

My question is that whether there is a plan to fix it or this can only be left as one limitation of scala?

@scabug
Copy link
Author

scabug commented Aug 27, 2011

Iskra Vitaly (iskra.vitaly) said:
As I understand, there are always 2 classes related to scala's object:

  1. The one having static methods implementations
  2. The one having instance methods delegating to corresponding statics

In some cases users of a object need to pass first class somewhere, in some second.
So why not make 2 distinct operators solely for objects instanceClassOf[] and staticClassOf[] to let the users to decide what do they need each time they need a class?

@scabug
Copy link
Author

scabug commented Aug 27, 2011

Hongxin Liang (honnix) said:
Hmm, really good suggestion. There should be good reasons to get both the static and non-static class of object.

@scabug
Copy link
Author

scabug commented Sep 15, 2011

Francois Armand (fanf) said:
The URL given in the first message now leads to a 404. That one still works: https://svn.apache.org/repos/asf/directory/apacheds/tags/1.5.2/core-integ/src/main/java/org/apache/directory/server/core/integ/annotations/Factory.java

@scabug
Copy link
Author

scabug commented Sep 11, 2012

@jsuereth said:
The inability to grab an object's class outside of the constructor is killing us here.

@scabug
Copy link
Author

scabug commented Sep 11, 2012

@retronym said:
I wonder, could {{classOf}} be rewritten these days as a macro? How about {{instanceClassOf}} / {{staticClassOf}}?

@scabug
Copy link
Author

scabug commented Jan 24, 2013

@retronym said:
https://groups.google.com/d/topic/scala-internals/D6LoxxxRbAA/discussion

scala> object O
defined module O

scala> def impl[T: c.WeakTypeTag](c: Context): c.Expr[Class[_]] = {import c.universe._; c.Expr[Class[_]](Literal(Constant(weakTypeOf[T])))}
impl: [T](c: scala.reflect.macros.Context)(implicit evidence$1: c.WeakTypeTag[T])c.Expr[Class[_]]

scala> def moduleClassOf[T <: Singleton]: Class[_] = macro impl[T]
moduleClassOf: [T <: Singleton]=> Class[_]

scala> moduleClassOf[O.type]
res5: Class[O.type] = class O$

@scabug
Copy link
Author

scabug commented Oct 22, 2015

tenstriker said:
even referencing scala class from java annotations might give an error. I am getting type mismatch error when referencing scala class that implements interface required by jsr-303 @constraint annotation.
for detail: http://stackoverflow.com/questions/33266551/jsr-303-constraintvalidator-in-scala-with-spring-mvc

@scabug
Copy link
Author

scabug commented Oct 22, 2015

@soc said:
I think this ticket demonstrates an issue, but we should think into which direction it should take us and adapt the ticket accordingly.

Should we come up with a way to refer to a companion symbol type as a constant (like companionOf\[T\]) or look into making SomeObject.getClass work as a constant literal?

@scabug scabug added this to the 2.13.0-RC1 milestone Apr 7, 2017
@adriaanm adriaanm modified the milestones: 2.13.0-RC1, Backlog Nov 13, 2018
@scala scala deleted a comment from scabug Mar 3, 2020
@scala scala deleted a comment from scabug Mar 3, 2020
@octonato
Copy link

octonato commented Aug 28, 2020

For the record, I run into that issue when using case objects with Jackson annotations.

Jason's example didn't work out-of-the-box (it dates from 2013), but needed some small adaptations. Here is what is needed when using more recent version of the macro lib.

import scala.reflect.macros.whitebox.Context
import scala.language.experimental.macros

object ObjectClassType {

  def impl[T: c.WeakTypeTag](c: Context): c.Expr[Class[T]] = {
    import c.universe._
    c.Expr[Class[T]](Literal(Constant(weakTypeOf[T])))
  }

  def classOf[T <: Singleton]: Class[T] = macro impl[T]
}

@SethTisue SethTisue added the fixed in Scala 3 This issue does not exist in the Scala 3 compiler (https://github.com/lampepfl/dotty/) label Nov 9, 2020
@SethTisue SethTisue modified the milestones: Backlog, 2.13.4 Nov 9, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
annotations fixed in Scala 3 This issue does not exist in the Scala 3 compiler (https://github.com/lampepfl/dotty/) has PR
Projects
None yet
Development

No branches or pull requests

4 participants