java 中调用scala_Scala中删除了Java的哪些功能?

java 中调用scala

尽管与Java相比语法更复杂,更不直观,但是Scala实际上放弃了Java的几个功能,有时是很好的,有时则提供标准库级别的替代。 您将很快看到,Scala不是Java的超集(如Groovy),实际上消除了很多噪音。 以下是缺少功能的目录。

中断并继续循环

每次我看到这样的代码:

while(cond1) {
    work();
    if(cond2)
        continue;
    rest();
}

我觉得这好像是一个真正错过了goto尚未被认为有害的人写的。 举起手来谁发现这个版本更具可读性:

while(cond1) {
    work();
    if(!cond2)
        rest();
}

摆脱中断还需要更多,但是通常将循环提取到单独的方法/函数(或至少将其放在现有方法的末尾)并使用return可以解决问题。 为何Scala允许您在其他函数中定义函数,所以您不会使用仅使用一次的大量小方法来污染全局类名称空间-当认真地提取Java中的方法时,有时会出现此问题。

打破并继续 –我们以祖父母的名义感谢您为命令式编程所做的贡献。 但是我们不再需要您,我们不会想念您。

数组

这些年来,我们已经学到了多少坏习惯,以及如何习惯那些前后矛盾,痛苦难懂的习语,这真是令人惊讶。 您在Java中拥有带有方括号语法, 长度 final属性和存储原始类型的能力的协变数组。 您还具有带有List <t>抽象的Java集合框架–它不是协变的,使用get()和size()方法并且不能存储基元。 差异列表不止于此,但是不是每个数组都只是List的特例吗? 当在语言之上实现集合时,为什么在语言中对数组有特殊的语法? 始终将它们从一种转换为另一种是否有点烦人?

String[] array = new String[10];
List<string> list = Arrays.asList(array);
String[] array2 = list.toArray(new String[list.size()]);

从集合到数组的转换是我最喜欢的Java习惯用法…为什么不仅具有相同的语法,相同的方法,相同的抽象,多态的行为—而且只有不同的实现名称?

val array = Array("ab", "c", "def")
println(array(1))
array filter (_.size > 1)

val list = List("ab", "c", "def")
println(list(1))
println(list filter (_.size > 1))

不用担心,Scala编译器在后台将使用与在Java中使用普通数组相同的有效数组字节码。 没有魔术抽象和多层包装。

原语

另一个奇怪的Java不一致-为什么我们要在原始int和包装Integer之间进行选择? 如果变量是Integer类型的,这是否意味着它是可选的(空),或者仅仅是您不能在集合中使用基元(如上所述,而是可以在数组中使用)? 这个拆箱安全 (也称为:这到底会引发NullPointerException吗? )我可以使用==运算符将它们与整数进行比较吗? 我可以简单地调用toString()来获取此数字的字符串表示形式吗?

在Scala中,您不再具有选择权,每个基本类型都是一个对象,而大多数时候大多数情况下仍是内存和字节码中的一个基本类型。 那怎么可能? 看下面的流行示例:

val x = 37     //x and y are objects of type Int
val y = 5
val z = x + y  //x.+(y) - yes, Int class has a "+" method
assert(z.toString == "42")

x,y和z是Int类型的实例。 它们都是对象,即使在语义上加两个整数也是x上带有y参数的方法+。 如果您认为它必须表现出色-再次在幕后将其编译为普通的原始加法。 但是现在,您可以轻松地在集合中使用基元,在需要任何类型(Java中的Object,Scala中的Any)时传递它们,或者简单地创建文本表示形式而无需笨拙的Integer.toString(7)习惯用法。 过多的不良习惯。

检查异常

我几乎不会错过的另一个功能。 这里没有太多要说的。 除Java之外,没有任何主流语言都提供它们,也没有任何主流JVM语言(Java除外)。 这个主题仍然是一个有争议的话题,但是,如果您曾经尝试处理无处不在SQLException或IOException,那么您就会知道它引入了多少样板而没有充分的理由。 无论如何,请看下面的示例…

介面

这个好! Scala没有接口。 相反,它引入了特性-抽象类(某些特性方法可能具有实现)和接口(一个可以混入多个特性)之间的某种特性。 因此,本质上,特征使您可以实现多重继承,同时避免可怕的钻石问题 。 如何做到这一点需要自己撰写一篇文章(简而言之:最后一个特征胜出),但我想向您展示一个示例,说明如何减少性状有帮助。

假设您正在编写抽象二进制协议的接口。 大多数实现采用原始字节数组,因此在Java中,您只需说:

public interface Marshaller {
    long send(byte[] content);
}

从实现的角度来看,这是很棒的-只需实现一个方法,抽象就可以了。 但是,该界面的用户抱怨它笨重且不太方便。 他们想发送字符串,二进制和文本流,序列化的对象等。 他们可以围绕此界面创建外观(每个用户将使用一组独特的错误来创建自己的界面),也可以强制API的作者对其进行扩展:

public interface Marshaller {

    long send(byte[] content);

    long send(InputStream stream);

    long send(Reader reader);

    long send(String s);

    long send(Serializable obj);

}

现在,API轻而易举,但是每个实现都必须实现五个方法,而不是一个。 还要注意,由于大多数抽象协议都是基于字节数组的,因此所有方法都可以按照第一个方法实现。 并且只有第一个包含实际的编组代码。 反过来,这导致每个实现都具有完全相同的四种方法-复制并没有消失-它只是被移动了。 实际上,这个问题被称为瘦与富接口,并且在Scala的Programming》一书中对此进行了描述。 我通常要做的是为服务提供者提供一个抽象类,其中包含除根目录以外的所有方法的典型实现,而根目录被所有其他方法使用:

import org.apache.commons.io.IOUtils;

public abstract class MarshallerSupport implements Marshaller {

    @Override
    public abstract long send(byte[] content);

    @Override
    public long send(InputStream stream) {
        try {
            return send(IOUtils.toByteArray(stream));
        } catch (IOException e) {
            throw new RuntimeException(e);  //choose something more specific in real life
        }
    }

    @Override
    public long send(Reader reader) {
        try {
            return send(IOUtils.toByteArray(reader));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public long send(String s) {
        try {
            return send(s.getBytes("UTF8"));
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public long send(Serializable obj) {
        try {
            final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
            new ObjectOutputStream(bytes).writeObject(obj);
            return send(bytes.toByteArray());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

}

现在,每个人都很高兴–而不是一遍又一遍地复制所有重载的方法,而只是MarshallerSupport的子类并实现您所需的东西。 但是,如果您的接口实现还必须继承某些其他类怎么办? 那你真不走运 但是,在Scala中,您将接口更改为特征,从而打开了混入(在扩展和实现之间进行思考)其他几个特征的可能性。 顺便说一句,您还记得我对检查异常的看法吗?

trait MarshallerSupport extends Marshaller {

    def send(content: Array[Byte]): Long

    def send(stream: InputStream): Long = send(IOUtils.toByteArray(stream))

    def send(reader: Reader): Long = send(IOUtils.toByteArray(reader))

    def send(s: String): Long = send(s.getBytes("UTF8"))

    def send(obj: Serializable): Long = {
        val bytes = new ByteArrayOutputStream
        new ObjectOutputStream(bytes).writeObject(obj)
        send(bytes.toByteArray)
    }
}

切换语句

Scala中没有switch语句。 调用模式匹配更好的开关将是亵渎神灵。 不仅因为Scala中的模式匹配是一个返回值的表达式,而且还不是因为您可以根据需要切换任何值。 甚至不是因为没有失败,突破和违约。 这是因为Scala的模式匹配使您甚至可以使用通配符来匹配整个对象结构和列表。 考虑这种表达简化方法,该方法最初取自已在Scala书中提到的Programming

abstract class Expr
case class Var(name: String) extends Expr
case class Number(num: Double) extends Expr
case class UnOp(operator: String, arg: Expr) extends Expr
case class BinOp(operator: String, left: Expr, right: Expr) extends Expr

//...

def simplify(expr: Expr): Expr = expr match {
    case UnOp("-", UnOp("-", e)) => e  //double negation
    case BinOp("+", e, Number(0)) => e //adding zero
    case BinOp("*", e, Number(1)) => e //multiplying by one
    case _ => expr
}

仔细看一下这段代码有多聪明! 如果我们的表达式是一元“-”运算,并且参数是第二个一元“-”运算,并且将任何表达式e作为参数(请考虑:-(-e)),则只需返回e即可。 如果发现此模式匹配示例难以阅读,请查看大致等效的Java代码。 但是请记住:大小无关紧要(Perl单行代码可能会做同样的事情)–它与可读性和可维护性有关:

public Expr simplify(Expr expr) {
    if (expr instanceof UnOp) {
        UnOp unOp = (UnOp) expr;
        if (unOp.getOperator().equals("-")) {
            if (unOp.getArg() instanceof UnOp) {
                UnOp arg = (UnOp) unOp.getArg();
                if (arg.getOperator().equals("-"))
                    return arg.getArg();
            }
        }
    }
    if (expr instanceof BinOp) {
        BinOp binOp = (BinOp) expr;
        if (binOp.getRight() instanceof Number) {
            Number arg = (Number) binOp.getRight();
            if (binOp.getOperator().equals("+") && arg.getNum() == 0 ||
                    binOp.getOperator().equals("*") && arg.getNum() == 1)
                return binOp.getLeft();
        }
    }
    return expr;
}

实例/广播

与许多其他功能一样,Scala没有用于instanceof和downcast的内置语法。 相反,该语言为您提供了有关实际对象的方法:

val b: Boolean = expr.isInstanceOf[UnOp]
val unOp: UnOp = expr.asInstanceOf[UnOp]

在Scala中,通常被视为语言一部分的许多功能实际上是在语言本身中实现的,或者至少它们不需要特殊的语法。 我喜欢这个想法,实际上,我发现Ruby创建对象的方法(Foo.new-方法而不是new运算符)非常有吸引力,甚至缺少在Smalltalk中的条件是否需要注意的情况

枚举

Scala没有对枚举的内置支持。 众所周知,Java枚举具有一些精美的功能,其他语言羡慕这些功能,例如类型安全性和向每个枚举添加方法的能力。 在Scala中至少有两种方法来模拟枚举:

object Status extends Enumeration {
   type Status = Value

   val Pending = Value("Pending...")
   val Accepted = Value("Accepted :-)")
   val Rejected = Value("Rejected :-(")
}

assume(Status.Pending.toString == "Pending...")
assume(Status.withName("Rejected :-(") == Status.Rejected)

或者,如果您不关心文本枚举表示形式:

object Status extends Enumeration {
   type Status = Value

   val Pending, Accepted, Rejected = Value
}

但是,模拟枚举的第二种也是最全面的方法是使用用例类。 旁注:name实际上是基类中定义的抽象方法。 当您声明一个方法而不定义方法主体时,它被隐式假定为抽象–无需使用额外的关键字标记明显的对象:

sealed abstract class Status(val code: Int) {
 def name: String
}

case object Pending extends Status(0) {
 override def name = "?"
}

case object Accepted extends Status(1) {
 override def name = "+"
}

case object Rejected extends Status(-1) {
 override def name = "-"
}

//...

val s: Status = Accepted

assume(s.name == "+")
assume(s.code == 1)

s match {
    case Pending =>
    case Accepted =>
    case Rejected =>  //comment this line, you'll see compiler warning
}

这种方法虽然与枚举本身无关,但具有许多优点。 最大的问题是,编译器在执行非穷举模式匹配时会警告您–思考:切换Java中的枚举而不显式引用每个值或默认块。

静态方法/字段

Scala没有静态字段和方法的概念。 相反,它具有名为对象(而不是类)的功能。 当使用object关键字定义一个类时,Scala运行时会急切地创建该类的一个实例,并使其在类名下可用。 这本质上是语言内置的单例模式,但最重要的是这种方法引入的思维方式转变。 而不是在类(在这种情况下只是事实上的名称空间)中人为地聚集在一起的一堆静态函数,而是拥有带有真实方法的单例:

sealed abstract class Status
case object Pending extends Status
case object Accepted extends Status
case object Rejected extends Status

case class Application(status: Status, name: String)

object Util {

    def groupByStatus(applications: Seq[Application]) = applications groupBy {_.status}

}

这是语法的工作方式(以及出色的ScalaTest DSL示例):

@RunWith(classOf[JUnitRunner])
class UtilTest extends FunSuite with ShouldMatchers {

   type ? = this.type

   test("should group applications by status") {
      val applications = List(
         Application(Pending, "Lorem"),
         Application(Accepted, "ipsum"),
         Application(Accepted, "dolor")
      )

      val appsPerStatus = Util.groupByStatus(applications)

      appsPerStatus should have size (2)
      appsPerStatus(Pending) should (
            have size (1) and
            contain (Application(Pending, "Lorem"))
      )
      appsPerStatus(Accepted) should (
            have size (2) and
            contain (Application(Accepted, "ipsum")) and
            contain (Application(Accepted, "dolor"))
      )
   }
}

volatile / transient / native和serialVersionUID消失了

语言设计师决定将前三个关键字转换为注释。 两种方法各有利弊,很难找到明确的赢家。 但是,将serialVersionUID转换为类级别的注解是一个不错的选择。 我知道这个领域在Java语言引入注解之前就已经存在了,所以我们不应该怪它。 但是我总是讨厌在静态类型语言中某些名称/字段具有特殊含义,除了语言规范本身( 魔术数字? )以外,其他地方都没有体现出来不幸的是,在Scala中也有这种不愉快行为的示例,即对apply()方法的特殊处理和以冒号结尾的方法。 太糟糕了。

前/后增量

您无法在Scala中执行i ++++ i 。 期。 您需要更加冗长的i + = 1 –更糟糕的是,该表达式返回Unit (认为​​: void )。 我们该如何处理此明显的功能缺失? 事实证明,这种类型的构造通常是命令式样式的遗产,可以通过使用功能更强大的纯构造轻松避免。 以以下问题为例:

您有两个大小相同的数组:一个带有名称,另一个带有年龄。 现在,您要显示每个名称的相应年龄-以某种方式并行遍历两个数组。 在Java中,很难实现干净整洁:

String[] names = new String[]{"Alice", "Bobby", "Eve", "Jane"};
Integer[] ages = new Integer[]{27, 31, 29, 25};

int curAgeIdx = 0;
for (String name : names) {
    System.out.println(name + ": " + ages[curAgeIdx]);
    ++curAgeIdx;
}

//or:

for(int idx = 0; idx < names.length; ++idx)
    System.out.println(names[idx] + ": " + ages[idx]);
}

在Scala中,它可能更短,但一开始非常神秘:

var names = Array("Alice", "Bobby", "Eve", "Jane")
var ages = Array(27, 31, 29, 25)

names zip ages foreach {p => println(p._1 + ": " + p._2)}

邮编 ? 我鼓励您对此示例有所了解。 如果您不想启动整个IDE,请尝试使用Scala REPL:

$ scala
scala> Array("one", "two", "three") zip Array(1, 2, 31)   
res1: Array[(java.lang.String, Int)] = Array((one,1), (two,2), (three,31))

仔细观察,您是否看到结果数组包含来自“ 压缩 ”在一起的第一和第二个数组的对应元素对? 一个简单的实验,现在突然比普通的命令式解决方案更清晰易读。

Scala发明者对Java语言非常了解,他们不仅添加了语法糖(例如函数文字或隐式转换)。 他们发现了Java中的许多不一致之处和烦恼,摆脱了它们,并提供了更简洁,更刻意的替换。 尽管像原始对象和数组对象这样的高级结构,在引擎盖下仍会生成相同的快速直接的字节码。

参考: Scala中删除了Java的哪些功能? 来自我们的JCG合作伙伴Tomek NurkiewiczNoBlogDefFound

编码愉快! 不要忘记分享!

相关文章:


翻译自: https://www.javacodegeeks.com/2011/08/what-features-of-java-have-been-dropped.html

java 中调用scala

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值