Scala collection class that can extend itself? AbstractSeq confusion - scala

Inside the Scala standard library, I noticed this:
package scala
package collection
package mutable
import generic._
...
/** Explicit instantiation of the `Seq` trait to reduce class file size in subclasses. */
abstract class AbstractSeq[A] extends scala.collection.AbstractSeq[A] with Seq[A]
AbstractSeq[A] extends AbstractSeq[A] with Seq[A]?
What is going on here?

It is extending scala.collection.AbstractSeq, while the definition is of scala.collection.mutable.AbstractSeq, so those are different classes. (Note the different package names: scala.collection vs. scala.collection.mutable.)

They are different classes. The one you are looking at is scala.collection.mutable.AbstractSeq, the one it is extending is scala.collection.AbstractSeq.
Package is a kind of namespace. Different classes can have the same name as long as they are in different packages.

Related

Why does class name of a type in a package object contain "package" in runtime class name?

I'm trying to generate the runtime class name of a class that is defined in a package object.
Example:
package com.foo
package object bar {
case class MyCaseClass()
}
import bar._
MyCaseClass().getClass.getCanonicalName
The above will generate com.foo.bar.package.MyCaseClass
If I use WeakTypeTag it will correctly generate the type as com.foo.bar.MyCaseClass.
package com.foo
trait MyTrait
case class MyImpl extends MyTrait
def getType[T](t: T)(implicit weakTypeTag WeakTypeTag[T]): String = {
weakTypeTag.tpe.fullName
}
What is the reason for the above difference in behavior? I know I must be missing something about the Scala type system...
This isn't so much about the type system as about the encoding of package objects on the JVM. The JVM doesn't have package-level methods, for example, so the Scala compiler has to create a synthetic class that has static methods, inner classes, etc. corresponding to the definitions in the package object. That class is named package, an arbitrary but self-explanatory name that has the advantage of being a keyword in both Scala and Java, so it's unlikely to result in collisions with non-synthetic code.
Java's reflection APIs know nothing about Scala, so naturally they can't hide this encoding from you. When you call getClass.getCanonicalName you're seeing the actual class name, corresponding to the class file you'd find at com/foo/bar/package\$MyCaseClass.class when you compile your code.
Scala's reflection APIs do know about Scala's encoding of package objects, and they will hide the synthetic package class from you. This arguably makes sense, since the details of the encoding aren't in the spec (if I remember correctly?) and so may vary across platforms or language versions, etc.
The discrepancy is a little confusing, but this isn't the only time you'll run into differences—the Scala reflection API hides lots of mangling, etc. that Java reflection can't.

Implicit method that is matched by trait that's extended by a specific (case?) class

I have case classes BFile and AFile that both extend trait File. And case classes BDir and ADir that both extend trait Dir. The classes are instantiated as their trait type.
I am trying to write implicit move methods (located on an object outside the A/BFile and A/BDir classes) that are overloaded for each combination of A/BFile & A/BDir.
I am unsure how to do this such that a different .move method will be implicitly used based on the specific classes of File and Directory. Is this even possible?
The reason why I don't want these methods implemented inside the class is because it requires me to pull in additional dependencies to use the classes; and I am unable to always provide these dependencies.
I also don't want to settle for depending on only the class since I am trying to follow the dependency inversion principle.
Edit: I think I am looking for the following or some equivalent:
trait File {
...
def move(directory: the class implementing this that extends Dir): File
def copy(directory: the class implementing this that extends Dir): File
}
trait Dir {
...
}

Instantiating all classes that extend trait in Scala

So I'm building a library, and the problem I have is as follows:
I have a trait, such as
package my.library
trait Animal {
def randomFunctions
}
What I need to know is all the classes the consumer code has, that extend/implement said trait, such as
package code.consumer
case class Cat extends Animal
case class Dog extends Animal
So in summary: inside my library (which has the trait) I need to find out all classes (in consumer code) that extend/implement the trait.
I finally solved this by using reflections (https://github.com/ronmamo/reflections) with the following little snippet:
val reflection = new Reflections()
reflection.getSubTypesOf(classOf[Animal])
An option would be to use a sealed trait. This forces all implementations of the trait to reside in the same file as the trait was defined.
This would break your separation of consumer and library code but you would be sure to get all implementations.
The only other option I can think of is to use an IDE, like IntelliJ which has an option to find all implementation based on given trait.

Scala AbstractSeq[A] location

http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.List
sealed abstract class List[+A] extends AbstractSeq[A] with LinearSeq[A] with Product with GenericTraversableTemplate[A, List] with LinearSeqOptimized[A, List[A]]
Where is AbstractSeq[A] location?
It's exactly where it says it is (scala.collection.AbstractSeq). However, it's a package-private class, and that's probably why it doesn't appear in the API. Here's its definition from 2.10.2:
/** Explicit instantiation of the `Seq` trait to reduce class file size in subclasses. */
private[scala] abstract class AbstractSeq[+A] extends AbstractIterable[A] with Seq[A]
In addition to Swift Tomato's answer, a bit of background - not having the AbstractSeq would mean that the Scala compiler must instantiate bridge methods for methods in Seq for every collection class that extends trait Seq. This compiler trick is needed to support multiple inheritance on the JVM.
Having all the concrete collection extend AbstractSeq allows concrete collections to inherit those bridge methods like any other JVM method, so the compiler does not need to instantiate bridge methods in every concrete collection class -- the class-file sizes of those concrete collections are reduced.
This class is private and visible only in the scala package to avoid further convoluting people's understanding of the collections package.

I can haz no package-private class in Scala?

Sorry for the catchy title. ;-)
I want to create a package-private class with a package-private method in Scala, so my class looks somewhat like this:
package net.java.truevfs.ext.pace
import ...
private[pace] abstract class AspectController(controller: FsController)
extends FsDecoratingController(controller) {
private[pace] def apply[V](operation: => V): V
... // lots of other stuff
}
However, if I use javap to check what the Scala compiler effectively creates, I get something like this:
$ javap -classpath target/classes net.java.truevfs.ext.pace.AspectController
Compiled from "AspectController.scala"
public abstract class net.java.truevfs.ext.pace.AspectController extends net.java.truevfs.kernel.spec.FsDecoratingController implements scala.ScalaObject{
public abstract java.lang.Object apply(scala.Function0);
...
}
This means that although the Scala compiler might respect the access restrictions, I could still call this class from any Java code, which is a clear encapsulation violation.
Am I missing something?
Is there a way to make this work as intended?
In addition to #RĂ©gis' answer, the reason Scala compiler doesn't make the class package-private is because by Scala rules it can be accessed from other packages: namely, subpackages of net.java.truevfs.ext.pace. E.g.
package net.java.truevfs.ext.pace.subpackage
import net.java.truevfs.ext.pace.AspectController
class Subclass extends AspectController { ... }
is legal in Scala, but in Java classes from net.java.truevfs.ext.pace.subpackage can't access package-private classes from net.java.truevfs.ext.pace.
You are not missing anything.
Many of the access restricitons in scala have no equivalent in java nor at the jvm level. The additional information is obviously right there in the .class file, but is there as custom annotations that only the scala compiler will interpret.
The scala object model can only partly be matched to the jvm object model, and a java compiler will only see this partial model.
I'd say that the match is pretty close and the scala compiler does a very good job at java interoperability, but nothings's perfect.
Not really a 100% correct answer...
You can make a package object if I want to do some fancy stuff in there with a private class. The package object is accessed like any other package.
The class MyClass is package private to that package object.
It's not package private however.
package object com.jasongoodwin.foo {
private class MyClass
class AnotherClass {
val myClass = new MyClass
}
}