Scala Wrapper class by extending Component and with the SequentialContainer.Wrapper trait, do I have the correct understanding of traits?

StackOverflow https://stackoverflow.com/questions/21750459

Question

The following code was taken from this post: How to create Scala swing wrapper classes with SuperMixin?

import scala.swing._
import javax.swing.JPopupMenu

class PopupMenu extends Component with SequentialContainer.Wrapper {
  override lazy val peer: JPopupMenu = new JPopupMenu with SuperMixin

  def show(invoker: Component, x: Int, y: Int): Unit = peer.show(invoker.peer, x, y)
}

I've been trying to make custom wrappers so need to understand this, which is simple enough but since I'm only starting to get acquainted with Scala so I'm a little unsure about traits. So what I've been hearing is that traits is like multiple inheritance and you can mix and match them?

I've drawn a diagram representing where PopupMenu sits within the whole inheritance structure. Just to clarify a few things:

1) It seems to override lazy val peer:JComponent from Component and also gets the contents property from SequentialContainer.Wrapper? (purple text) Is that right?

2) Sequential.Wrapper also has a abstract def peer: JComponent.. but this isn't the one being overriden, so it isn't used at all here?

3) What's confusing is that Component and Sequential.Wrapper have some identical properties: both of them have def publish and def subscribe (red text).. but the one that the popupMenu will use is subscribe/publish from the Component class?

4) why can't we write PopupMenu extends SequentialContainer.Wrapper with Component instead?

Hopefully that isn't too many questions at once. Help would be much appreciated, I'm a beginner to Scala.. diagram representing the inheritance/trait relationship leading up to popupMenu

Was it helpful?

Solution

I'll answer using the numbers of your questions:

  1. Correct

  2. Correct. The top trait is UIElement which defines an abstract member def peer: java.awt.Component. Then you have Container which merely adds abstract member def contents: Seq[Component] to be able to read the child components. Container.Wrapper is a concrete implementation of Container which assumes (abstractly) the Java peer is a javax.swing.JComponent. Note that in Java's own hierarchy, javax.swing.JComponent is a sub-type of java.awt.Component, so there is no conflict. Sub-types can refine the types of their members ("covariance"). SequentialContainer refines Container by saying that contents is a mutable buffer (instead of the read-only sequence). Consequently, its implementation SequentialContainer.Wrapper mixes in Container.Wrapper but replaces the contents by a standard Scala buffer. At no point has a concrete peer been given, yet. For convenience, Component does implement that member, but then as you have seen, the final class PopupMenu overrides the peer. Because of the way the type system works, all the participating traits can access peer, but only PopupMenu "knows" that the type has been refined to be javax.swing.JPopupMenu. For example SequentialContainer.Wrapper only knows there is a javax.swing.JComponent, and so it can use this part of the API of the peer.

  3. The Publisher trait is introduced by UIElement, so you will find it in all types deriving from UIElement. There is nothing wrong with having the same trait appear multiple times in the hierarchy. In the final class, there is only one instance of Publisher, there do not exist multiple "versions" of it. Even if Publisher had not been defined at the root, but independently in for example Component and SequentialContainer.Wrapper, you would only get one instance in the final class.

  4. This is an easy one. In Scala you can only extend one class, but mix in any number of traits. Component is a class while all other things else are traits. It's class A extends <trait-or-class> with <trait> with <trait> ....


To sum up, all GUI elements inherit from trait UIElement which is backed up by a java.awt.Component. Elements which have child elements use trait Container, and all the normal panel type elements which allow you to add and remove elements in a specific order use SequentialContainer. (Not all panels have a sequential order, for example BorderPanel does not). These are abstract interfaces, to get all the necessary implementations, you have the .Wrapper types. Finally to get a useable class, you have Component which extends UIElement and requires that the peer is javax.swing.JComponent, so it can implement all the standard functionality.

When you implement a new wrapper, you usually use Component and refine the peer type so that you can access the specific functionality of that peer (e.g. the show method of JPopupMenu).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top