تحياتي يا هبر! أوجه انتباهكم إلى مقالة قصيرة يوم الجمعة حول Java و Scala والمبرمجين المجانين والوعود المحطمة.
تؤدي الملاحظات البسيطة أحيانًا إلى أسئلة غير بسيطة للغاية.
هنا ، على سبيل المثال ، هي حقيقة بسيطة وخارجية ، وربما حتى تافهة تنص على أنه في Java يمكنك توسيع أي فئة غير نهائية وأي واجهة في النطاق. وآخر ، بسيط أيضًا ، يقول أن Scala code الذي تم تجميعه لـ JVM يمكن استخدامه من Java code.
ومع ذلك ، فإن الجمع بين هاتين الحقائقين جعلني أتساءل: كيف ستتصرف أي فئة من وجهة نظر جافا ، التي يتم إغلاقها في سكالا ، أي لا يمكن توسيعه برمز خارجي متعلق بالملف الخاص به؟
فئة سكالا مترجمة من وجهة نظر الفنان. المصدر: https://specmahina.ru/wp-content/uploads/2018/08/razobrannaya-benzopila.jpg
كأرنب تجريبي ، أخذت الصف القياسي Option
. من خلال إطعامها إلى أداة التفكيك المدمجة في IntelliJ Idea ، نحصل على شيء مثل هذا:
public abstract class Option
implements IterableOnce, Product, Serializable {
public abstract Object get();
}
ومع ذلك ، لن يكون رمز Decompiled رمز Java صالحًا - مشكلة مشابهة لتلك الموضحة هنا ، على سبيل المثال ، مع هذه الطريقة:
public List toList() {
return (List)(
this.isEmpty()
? scala.collection.immutable.Nil..MODULE$
: new colon(this.get(), scala.collection.immutable.Nil..MODULE$)
);
}
MODULE$ , package object. , , Java , ?
, , ...
Java ( Maven), Scala provided- — , , , :
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>2.13.1</version>
<scope>provided</scope>
</dependency>
, scala.Option
. Idea, , Scala , sealed-, , , :
package hack;
public class Hacking<T> extends scala.Option<T> {
@Override
public T get() {
return null;
}
public int productArity() {
return 0;
}
public Object productElement(int n) {
return null;
}
public boolean canEqual(Object that) {
return false;
}
}
- , Option Product.
, — . mvn package
— , , jar-, , Java, , , .
, Scala.
… scala-
Scala (, SBT) — lib , , , ; build.sbt , Idea. ( ) :
import hack.Hacking
object Main {
def main(args: Array[String]): Unit = {
implicit val opt: Option[String] = new Hacking()
}
private def tryDo[T](action: => T): Unit = {
try {
println(action)
} catch {
case e: Throwable => println(e.toString)
}
}
}
implicit var
.
tryDo
— , : , , . call-by-name tryDo
, , .
, match
— , sealed class- ( , sealed-, ?)
object Main {
def main(args: Array[String]): Unit = {
tryMatch
}
private def tryDo[T](action: => T): Unit = {
}
private def tryMatch(implicit opt: Option[String]): Unit = tryDo {
opt match {
case Some(inner) => inner
case None => "None"
}
}
}
:
scala.MatchError: hack.Hacking
, : Option — sealed class, ( case Some case None), :
[warn] $PATH/Main.scala:22:5: match may not be exhaustive.
[warn] It would fail on the following input: None
[warn] opt match {
[warn] ^
, .
, - Option:
object Main {
def main(args: Array[String]): Unit = {
tryMap
}
private def tryDo[T](action: => T): Unit = {
}
private def tryMap(implicit opt: Option[String]): Unit =
tryDo(opt.map(_.length))
}
:
java.lang.NullPointerException
, map:
sealed abstract class Option[+A] /* extends ... */ {
final def isEmpty: Boolean = this eq None
def get: A
@inline final def map[B](f: A => B): Option[B] =
if (isEmpty) None else Some(f(this.get))
}
, :
- — None. , isEmpty false.
- , this.get, null.
- null , — length.
- length null NPE.
, NPE , Java ? (, , , ...)
:
object Main {
def main(args: Array[String]): Unit = {
tryContainsNull
}
private def tryDo[T](action: => T): Unit = {
}
private def tryContainsNull(implicit opt: Option[String]): Unit =
tryDo(opt.contains(null))
}
(, , null) , contains -Nullable . , , , Option false — null. ?
:
true
, , , contains map: !isEmpty && this.get == elem
.
, . , null , Option ( , , ) else match.
في الواقع ، كل ما كانت هناك حاجة إليه لهذه المقالة كان تجربة صغيرة للكشف عن فارق بسيط واحد لتفاعل اللغات المختلفة على JVM واحد. الفروق الدقيقة ، مع القليل من التفكير ، واضحة ، ولكن - لذوقي ، لا تزال مثيرة للاهتمام.