ist an diesem von einer kleinen anderen Perspektive betrachten lassen: was eigentlich ist ein Parameter? Was heißt das?
Und beginnen wir mit etwas, mit dem Sie wahrscheinlich vertrauter sind: Wertparameter. Was bedeutet ein Wertparameter in einem Unterprogramm? Sprich, wir haben so etwas wie diese:
def foo(a, b) = { /* … */ } // deliberately ignoring types and the body
Es bedeutet: Ich habe ein Unterprogramm foo
mit zwei Parametern, a
und b
. Und das Unterprogramm ist allgemein, es ist egal, was die tatsächlichen konkrete Werte von a
und b
sind. Es funktioniert für alle Werte von a
und b
weil es nicht wissen, was die Werte a
und b
sind.
Etwas konkreter:
def plus(a: Int, b: Int) = a + b
Wieder plus
nicht weiß, was die tatsächlichen Werte von a
und b
sind. Es funktioniert für 2
und 3
ebenso wie für 23
und 42
oder 0
und 0
. Es ist völlig und völlig unwissend über die konkreten Werte von a
und b
. Es weiß nur, dass a
und b
existieren.
Jetzt Typ Parameter sind das gleiche, nur auf der Typenebene statt der Wertstufe.
blabla[A]
bedeutet dasselbe auf der Typebene als blabla(a)
auf dem Wert Ebene bedeutet: „Ich habe eine Sache haben, ich habe keine Ahnung, was es ist (und ich interessiere mich nicht), aber ich rufe es A
. "
Also, was Sie effektiv getan haben, ist, Scala zu sagen, dass Sie nichts über A
wissen.Aber dann machst du etwas damit (oder vielmehr mit seiner Instanz): du fügst es hinzu. Aber, wie weißt du überhaupt kann es hinzufügen? Du weißt nicht einmal was es ist!
Also, es hinzufügen kann nicht möglicherweise arbeiten, muss es müssen fehlschlagen!
Allerdings ist die Weg es fehlschlägt etwas seltsam ist, und hat mit einigen der vordefinierten impliziten Konvertierungen im scala.Predef
Objekt zu tun. Insbesondere, there is an implicit conversion for string concatenation, which can convert an arbitrary object into a String
and then concatenate another String
to it. In diesem Fall konvertiert es a
in any2stringadd
und versucht dann, b
hinzuzufügen, aber die any2stringadd.+
Methode nimmt nur ein String
als sein Argument, und damit erhalten Sie die seltsame Fehlermeldung, dass es eine String
erwartet.
Es gibt ein paar andere ähnliche Typen, die manchmal auftauchen, wenn Sie komplexe Typfehler haben:
Any
, AnyRef
, AnyVal
: diese Typen an der Spitze der Scala Typ Hierarchie sitzen. Manchmal, wenn Sie einen Programmierfehler haben, wo Sie denken, dass Sie den gleichen Typ aus zwei verschiedenen Codepfaden zurückgeben, aber Sie tatsächlich zwei verschiedene Typen zurückgeben, wird Scala trotzdem versuchen, den gemeinsamen Typ zwischen den beiden abzuleiten, und endet mit dem nur gemeinsamer Vorfahr ist Any
, AnyRef
oder AnyVal
. (Das wäre wie Object
in Java oder C♯.)
Serializable
: das ist eigentlich das Gleiche wie oben. Viele verschiedene Arten implementieren die Serializable
Schnittstelle, also manchmal, wenn Sie zwei sehr unterschiedliche Arten haben, wo Sie eigentlich den gleichen Typ erwarten, wird Scala hilfreich Serializable
als der nächste gemeinsame Vorfahre ableiten, und dann Sie anschreien, um Sachen mit einem Serializable
das zu tun Es wird nicht unterstützt.
Product
ist ein super-Merkmal aller Product
s (d.h. Product1
, Product2
, ... Product22
) und damit alle Tuple
s (d.h. Tuple1
, Tuple2
, ... Tuple22
). ist auch in alle case class
es gemischt. Also, wenn Sie zwei Tuple
s verschiedener arity oder zwei nicht verwandten case class
es (zB manchmal Sie None
zurück, aber dann versehentlich zurückgeben somethingOfTypeFoo
statt Some(somethingOfTypeFoo)
), dann die präziseste allgemeinste Art zwischen Option
und Ihre case class Foo
als Product
oder abgeleitet werden ...
Product with Serializable
: es ist auch möglich, eine Kombination der oben genannten zu erhalten. Z.B. Tuple
s sind Serializable
, also dieser ist eigentlich der genaueste gemeinsame Typ von zwei Tuple
s unterschiedlicher Arity.
Eine übliche Art und Weise in diese Probleme zu laufen ist in einem bedingten Ausdruck ohne else
:
if (true) 42
welche Art diese Rückkehr tut? Int
? Nein! Der Zweig then
gibt Int
zurück, aber der Zweig else
gibt nichts zurück (weil es keine else
Verzweigung gibt).Scala hat tatsächlich einen Rückgabetyp, um nichts zurückzugeben: Unit
. Unit
ist ein Subtyp von AnyVal
, Int
ist ein Subtyp von AnyVal
, so dass der nächste gemeinsame Vorfahr der Typen der beiden Zweige des bedingten Ausdrucks tatsächlich AnyVal
ist, nicht Int
. (Anmerkung: die Tatsache, dass der Zweig else
nicht erreichbar ist, ist aus einer Schreibperspektive irrelevant. Erreichbarkeit ist eine Laufzeit-Sache, Typen sind eine Kompilierzeit-Sache.)