scala中为什么不建议用return

本文探讨了Scala中不推荐使用return关键字的原因,包括保持函数式编程风格、影响类型推断、可能导致返回含义模糊等问题,并提出了相应的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

scala中为什么不建议用return


在scala中使用return的话,编译的时候会提示the latest statement is method is automatically returned, use of th return keyword is redundant.这个警告是说最后一个语句的结果会自动返回,不需要使用return语句。
scala不建议使用return关键字,这一点在刚刚接触函数式编程的时候感觉非常难受。有return会让代码结果更清晰不是么?在查阅之后,整理了以下几点不建议使用return关键字的原因。

1.要写函数而不是指令

函数式编程的一个重要理念就是要尽量使代码由无状态的函数构成,而不是给计算机发出指令。例如

f(x) = x + 1    //式1.1

是一个函数。
scala会自动将最后一个表达式的结果作为返回结果

def f(x:Int) = x + 1    //式1.2
def f(x:Int):Int = return x + 1     //式1.3

从以上两种分别使用和不使用return的表达方式来看,不使用return看起来更接近函数,而使用了return则更像指令。

2.return影响类型推断

scala中的类型推断机制会自动将最后一个表达式的类型作为返回类型,例如式1.2中,函数能够自动识别结果为Int类型。如果使用了return语句,就会破坏类型推断机制,需要显式注明返回类型,例如式1.3。

3.使用return返回含义模糊

有时使用了return会让代码的返回更加混乱,这个歧义主要产生于return到底返回到哪一层函数。

def add(n:Int, m:Int): Int = return n + m     //式3.1
def sum1(ns: Int*): Int = ns.foldLeft(0)(add)    //式3.2

例如上述代码,目前来看还没有什么问题,但是如果写成下面的形式

def sum2(ns: Int*): Int = ns.foldLeft(0)((n,m) => return n+m)    //式3.3

直观感受式3.3与3.1+3.2应该是等效的。但事实上sum1(1,2,3) = 6,而sum2(1,2,3) = 1.
原因就是return语句会直接让它所出现的函数返回。也就会直接break foldLeft的循环返回结果。
再看另外一个例子:

def foo: Int = { 
    val sumR: List[Int] => Int = _.foldLeft(0)((n, m) => return n + m)
    sumR(List(1,2,3)) + sumR(List(4,5,6))
}

首先定义一个匿名函数,在调用匿名函数的时候,相当于return语句出现在了foo函数中。因此foo() = 1

4.NonLocalReturnControl

在scala的循环中的return实际上是通过抛异常实现的,编译后发现

return value

被编译成了

throw new NonLocalReturnControl(key/*metadata*/, value)

而NonLocalReturnControl的源码为:

class NonLocalReturnControl[@specialized T](val key: AnyRef, val value: T) extends ControlThrowable {
    final override def fillInStackTrace(): Throwable = this
}

可以看到NonLocalReturnControl异常继承了Throwable,并且为了提升性能重写了fillInStackTrace不填入堆栈信息。这样一来,如果我们在代码中为了保护代码不crash而这样写:

def fun(n:Int):String = {
    try {
        for(i <- 0 to n){
            if(i < 5){
            }else{
                return "gt"
            }
        }
        ""
    }catch{
        case t:Throwable => return t.toString
    }
}

最终得到的结果字符串则为scala.runtime.NonLocalReturnControl,并不是我们预期的结果。

5.应当怎么做

实际开发中我们会经常遇到貌似必须使用return的时候,那应该怎么办呢?首先,scala既然提供了return关键字,说明它并不是禁止使用,而是需要考虑清楚是否必须这么做。在scala认为,所有的需要使用return来break的循环,都是可以通过转化为递归来替代的,并且性能方面scala也专门为递归做了优化。

### 如何在 Scala 项目中引入并配置 `RedisTemplate` 使用 Redis #### 引入依赖 为了使 Scala 项目能够使用 Spring Data Redis 的功能,需要添加相应的 Maven 或 SBT 依赖。对于基于 SBT 构建的 Scala 应用程序来说,在构建文件 `build.sbt` 中加入如下依赖声明: ```scala libraryDependencies += "org.springframework.data" % "spring-data-redis" % "2.7.3" // 如果还需要其他 spring boot starter 可能也需要加上对应的版本号 ``` 此外,考虑到兼容性和性能优化方面的原因,建议同时引入 Lettuce 连接池的支持[^3]。 #### 配置 application.properties 文件 接着,在项目的资源目录下创建或编辑 `application.properties` 文件来指定 Redis 实例的相关参数: ```properties # Redis服务器地址 spring.redis.host=127.0.0.1 # Redis端口号,默认为6379 spring.redis.port=6379 # 数据库编号, 默认是0 spring.redis.database=0 # 下面这些是关于连接池的一些设置... spring.redis.lettuce.pool.max-active=20 spring.redis.lettuce.pool.max-idle=8 spring.redis.lettuce.pool.min-idle=0 ``` 以上配置项可以根据实际环境调整相应数值以满足需求[^4]。 #### 编写 Java/Scala 类实现业务逻辑 由于 Spring Framework 主要面向 Java 开发者设计,因此即使是在 Scala 项目里也通常采用混合编程的方式编写控制器和服务层组件。下面给出一段简单的例子展示如何利用 `@Bean` 注解定义一个自定义的 `RedisTemplate<String, Object>` Bean 并注入到服务类中完成 CRUD 操作: ```java @Configuration public class AppConfig { @Autowired private RedisConnectionFactory redisConnectionFactory; @Bean(name="stringObjectRedisTemplate") public RedisTemplate<String, Object> stringObjectRedisTemplate(){ final RedisTemplate<String,Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); template.setValueSerializer(jackson2JsonRedisSerializer); template.afterPropertiesSet(); return template; } } ``` 注意这里选择了 JSON 格式的序列化方式处理对象存储问题;如果只需要保存字符串类型的 key-value 对,则可以直接调用默认的方法即可[^1]。 最后一步就是在具体的 Service 层去运用这个已经装配好的模板来进行增删改查等动作了。比如像这样: ```scala @Service class MyService(@Autowired val redisTemplate: RedisTemplate[String, Any]) { def setValue(key: String, value:Any): Unit ={
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值