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

@BeanProperty interacts poorly with explicit setter/getters #2215

Open
scabug opened this issue Jul 30, 2009 · 3 comments
Open

@BeanProperty interacts poorly with explicit setter/getters #2215

scabug opened this issue Jul 30, 2009 · 3 comments

Comments

@scabug
Copy link

scabug commented Jul 30, 2009

There are two problems illustrated by this code:

a) @BeanProperty's setter can't delegate explicitly to the explicitly defined setter of the same property (it has no way of knowing what the name of the explicit setter is); therefore you'd need two setters and a delegate method to handle the common validation logic, which seems messy. The same applies to the getter, but this is less problematic.

b) @BeanProperty exposed setters and getters must have a different name than the explicit Scala style getter and setter, which causes messy APIs. See the class usage below.

class Goat {

    @scala.reflect.BeanProperty
    var hooves_ = 0  // note, cannot name it _hooves, because @BeanProperty objects
    
    def hooves:Int = hooves_
   
    def hooves_=(hooves:Int) {
      require(hooves < 5)
      hooves_ = hooves
    }
}

scala> val g = new Goat
g: Goat = Goat@19eef79

scala> g.setHooves_(12)
// works just fine
 
scala> g.hooves = 12
java.lang.IllegalArgumentException: requirement failed

scala> g.getHooves_
res9: Int = 12

scala> g.hooves
res10: Int = 12

==================================

Two suggested improvements:

  1. allow implicit getters in combination with explicit setters.
  2. if the above condition is detected, then @BeanProperty's setter should delegate to the explicit setter matching the name of the property.

This does not catch the rare case where the getter does special business logic, but it does make the more general pattern (setter with validation) much friendlier to use.

class Goat {

    @scala.reflect.BeanProperty
    private[this] var hooves = 0

    def hooves_=(hoovesV:Int) {
      require(hoovesV < 5)
      hooves = hoovesV
    }
}
@scabug
Copy link
Author

scabug commented Jul 30, 2009

Imported From: https://issues.scala-lang.org/browse/SI-2215?orig=1
Reporter: Reuben Firmin (reubenfirmin)

@scabug
Copy link
Author

scabug commented Sep 30, 2009

@lrytz said:
This is on hold until a good & feasible solution is proposed (http://www.nabble.com/-scala--properties---bean-properties-td25028167.html)

Another idea is to define the validation logic in a separate private method, e.g.

class A {
  @BeanProperty @preGet(preHoovesGet) @preSet(preHoovesSet) var hooves = 0
  private def preHoovesSet(h: Int) = {
    require(h < 5)
    h
  }
  private def preHoovesGet(h: Int) = {
    log("reading hooves: "+ h)
    h
  }
}

the generated getter / setter would then have the form (hooves denotes the actual field)

def hooves: Int = preHoovesGet(`hooves`)
def hooves_=(h: Int) { `hooves` = preHoovesSet(h) }

@scabug
Copy link
Author

scabug commented Mar 3, 2011

ddekany said:
How about simply supporting @BeanProperty on methods? If the annotated method looks like def someName: SomeType then it generates def getSomeName() = someName, and if the annotated method looks like def someName_=(val: SomeType): Unit then it generates def setSomeName(val: SomeType) { someName = val }. This would work even if there is no var that backs both methods, that is, for calculated properties. Annotated methods that doesn't follow any of the above patterns should cause a compilation error. (Later this could be extended to support indexed properties as well.)

A drawback is that it's a bit verbose in this common case (two @BeanProperty-es):

private _someName: SomeType
@BeanProperty def someName = _someName
@BeanProperty def someName_=(val: SomeType) {
  /* do some extra things here */
  _someName = val
}

But even this is better that we have now. To improve this further, there should be a standard way of naming those private vars (like _${propertyName}), because then one could just annotate the private var with @BeanProperty, and it could deduce the method names from the var name. If the var name doesn't follow the proper format or there's no matching method then that should be a compilation error, rather than a silent no-op.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants