3

I have a Lift project with mixed Java-Scala code. The project has a JPA backend written in Java with EclipseLink, which is accessed by the Scala side, which uses Lift and Lift-NG.

On the Java side, I have the following relevant interfaces:

interface IEntity

interface IDAO<T extends IEntity> {
    void persist(T t);
}

On the Scala side, I have the following:

abstract class Binding[T <: IEntity] extends NgModel {
    def unbind: T
}

class BasicService[B <: Binding[_ <: IEntity]](serviceName: String, dataAccessObject: IDAO[_ <: IEntity]) {
    def persist(binding : B): Unit = {
        val entity = binding.unbind
        dataAccessObject.persist(entity)
    }
}

The purpose of this hierarchy is to let me create Service instances which handle a given Binding for entity E, which can also receive and use an appropriate DAO created to handle type E. For example:

// Java side
class Todo implements IEntity

class TodoManager implements IDAO<Todo>

// Scala side
case class TodoBinding extends Binding[Todo] {
    override def unbind: Todo = new Todo()
}
object HelloWorld extends BasicService[TodoBinding]("todoService", new TodoManager) 

My problem is a compilation error that occurs inside the persist method of BasicService. On the last line, I get this:

Type mismatch: expected _$1, actual: IEntity

As I am a bit inexperienced with Scala, I might be missing something very obvious with the type system, but I genuinely cannot figure this out. How can I work around this error?

csvan
  • 8,782
  • 12
  • 48
  • 91

1 Answers1

3

Your definition of BasicService does not require that the type argument of B:

B <: Binding[_ <: IEntity]

is compatible with the type argument of dataAccessObject:

dataAccessObject: IDAO[_ <: IEntity]

What if one is Binding[FooEntity] and the other is IDAO[BarEntity]?

You should take that type variable E that you say you are trying to use, and actually define it:

class BasicService[E <: IEntity, B <: Binding[E]]
                  (serviceName: String, dataAccessObject: IDAO[E])

Also note that in the example code you posted, the type variable B to BasicService might not be necessary, because you only use it to receive the argument binding to persist(). You could have simply used the type Binding[E] there:

class BasicService[E <: IEntity]
                  (serviceName: String, dataAccessObject: IDAO[E]) {
  def persist(binding: Binding[E]): Unit = // ...
}
Dan Getz
  • 8,774
  • 6
  • 30
  • 64
  • Thanks, this indeed solves the problem. I am getting another error now ("No manifest available for B"), but it seems to be outside the scope of this question, as it occurs in a method I did not mention above. I will make a new question if necessary, and accept your answer as it is. – csvan Aug 13 '14 at 11:49
  • New question is here, for reference: http://stackoverflow.com/questions/25285865/no-manifest-available-for-type-with-type-parameter – csvan Aug 13 '14 at 12:13