简体   繁体   中英

Case class Members not accessible via Companion Object

Lets start off with a story
I am in contact with a fictious club namd Foo where people have registered for a party. The manager has asked me to maintain separate list of member names and member ids . The manager came to me one day and told about the problem. I as a developer playing around with scala gave him a solution like the aforementioned.


I wanted to maintain a class which will contain the name of the members and their ids. The code was very crude in nature and I absolutely had performed no validations
STEP 1: I created a class called NonEmptyFoo

 case class NonEmptyFoo(bar1:String,bar2:String) 

while drinking beer in the club, I found few people registering

STEP 2: Adding users (username,userid) both are String objects

 val member1=NonEmptyFoo("member1","Foo_member1") val member2=NonEmptyFoo("member2","Foo_member2") val member3=NonEmptyFoo("member3","Foo_member3") 


many people registered later, which was really good for the manager

STEP 3: Creating separate lists for username and userid

 val (memName,memId)=clubMembers map(x=>(x.bar1,x.bar2)) unzip 

Whoooah! my solution seemed to work unless one day the manager came and told me;

"Man! people are so excited, that at times, they are registering without even entering the data; I am not able to get the correct list of member names and their ids as the list have many empty fields. This got on my nerves.
I assured the manager of the club to provide him with a robust solution the next day.
Two things came to my mind
1 . If I write the code from scratch I need to remove the old code and the new code may not be readable
2 . I need to keep the code readable and make it in a way such that the new developer can find it easy to maintain the new code and add/remove features from it
At night I thought to add abstractions to it. I added the following code
STEP 1 Created a trait Foo with NonEmptyFoo for users with valid entry and EmptyFoo for people with invalid entries

  trait Foo case class EmptyFoo() extends Foo case class NonEmptyFoo(bar1:String,bar2:String) extends Foo object Foo{ def apply(bar1:String,bar2:String)= (bar1,bar2) match{ case (x,y)=>if(x.isEmpty||y.isEmpty) EmptyFoo() else NonEmptyFoo(x,y) case _=> EmptyFoo() } } 

The above code has benefits. First off I am able to validate users with valid entry and users with invalid entry. Secondly I am abstracting out the empty and non empty Foo to only Foo such that the new methods can easily be added into Foo and implemented thereby in the child classes which as a result hides the inner implementation.
As the user enters a valid data as under

 val mamber1=Foo("member1","Foo_member1") 

The output on the worksheet is being shown as

 member1: Product with Serializable with Foo = NonEmptyFoo(member1,Foo_member1) 

and, when someone misses to enter one of the fields as given below

 val member2=Foo("member2","") 

The output on the worksheet is being shown as

 member2: Product with Serializable with Foo = EmptyFoo() 

Interesting!! it works...
Please be little patient;
I am able to perform an abstraction and able to come out with a minimal yet good solution. But, I was facing real problem when I wrote the code as follows

    val member1=NonEmptyFoo("member1","Foo_member1")
    val member2=NonEmptyFoo("member2","Foo_member2")
    val member3=NonEmptyFoo("member3","Foo_member3")
    val clubMembers=List(member1,member2,member3)
    //heres where the problem occurs
    val (memName,memId)=clubMembers map(x=>(x.bar1,x.bar2)) unzip

Whooah! I got stuck here, because the compiler, in the lambda expression given below

 x=>(x.bar1,x.bar2) 

was not able to recognize bar1 and bar2 .

But, this solution worked well;

  val member1=NonEmptyFoo("member1","Foo_member1") val member2=NonEmptyFoo("member2","Foo_member2") val member3=NonEmptyFoo("member3","Foo_member3") val clubMembers=List(member1,member2,member3) //heres where the problem occurs val (memName,memId)=clubMembers map(x=>(x.bar1,x.bar2)) unzip 

Apparently, the solution violates abstraction since I am explicitly using the inner class names whereas I should have made use of the name of the trait from where based upon the validation, the compiler shall infer the need for instatiation of the objects as per the inner classes.

Could you please suggest what could be wrong with the new solution.
The reasons due to which the compiler did not recognize bar1 and bar2 will be highly appreciated.
Yes alternate solutions do exist yet I would love to be suggested with the problems existing solution and that may be followed by an alternate solution which completely upon your discretion.

Thanks in advance for help!

Inferred type of clubMembers is List[Foo] (even though concrete elements are instances of NonEmptyFoo ) and since Foo doesn't have bar1 and bar2 fields, you can't access them in your map invocation. One possible solution would be to add bar1 and bar2 to the Foo :

sealed abstract class Foo(val bar1: String, val bar2: String)
case object EmptyFoo extends Foo("", "")
case class  NonEmptyFoo(override val bar1: String, override val bar2: String) extends Foo(bar1, bar2)

object Foo {
  def apply(bar1: String, bar2: String): Foo = {
    if (bar1.isEmpty || bar2.isEmpty) EmptyFoo else NonEmptyFoo(bar1, bar2)
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM