开发者学堂课程【Scala 核心编程-基础:函数使用注意事项和细节1】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/609/detail/8933
函数使用注意事项和细节1
内容介绍
一、细节1-5
二、细节6-10
一、细节1-5
目前已经把基本语法和基本的运行原理讲解完毕,这节课将对函数注意事项和细节进行讲解。对于函数的注意细节一共总结了13点,下面进行函数注意事项和细节的讨论。(对于现在讲的函数也可以理解为方法,因为现在还没有学习函数编程,所以函数和方法本质上没有什么区别)
1、细节1
函数的形参列表可以是多个,如果函数没有形参,调用时可以不带()
2、细节2
形参列表和返回值列表的数据类型可以是值类型和引用类型。
(1)案例演示:
package com . atguigu . chapter05. fundetails
object Details01 {
def main(args: Array[string]): Unit = {
//形参列表和返回值列表的数据类型可以是值类型和引用类型
val tiger = new Tiger
//构建一个 tiger=new tiger
val tiger2 = test01(10, tiger)
//传入10, tiger,这里会返回一个 tiger,注意此处返回的 tiger 与值类型中返回的tiger 是同一个,然后进行接收 tiger。
println(tiger2.name)
// 此时进行输出 tiger2,因为返回的tiger与值类型中返回的 tiger 是同一个,而值类型中已经改名为 jack,所以输出应该也为 jack
println(tiger .name)
// 再输出 tiger 也为 jack,与 java 一样,就是创建了对象 tiger 然后改名字再返回,所以 tiger 和 tiger2是同一个对象
println(tiger .hashCode() +”“+ tiger2 . hashCode())
//那么在 scala 中也是同一个对象,这里打印出hashCode 进行证明
}
def test01(n1:Int,tiger:Tiger): Tiger= {
//这里为既可以传入一个 Int 值类型,也可以传入一个引用类型Tiger,返回也可以是 Tiger 类型。体现了形参列表可以是值类型也可以是引用类型,同样返回的也可以是值或者引用。因为值类型原先返回的 Int 已经讲过了,然后再进行如上引用类型的讲解。
println("n1=" + n1)
//打印 n1
tiger .name = "jack'
//将 tiger 名字改为 jack
tiger
//最后将 tiger 进行返回,这里 return 可写可不写
}}
class Tiger {
//引用类型 Tiger 类
//一个名字属性
var name=””
}
(2)运行结果:
D: \program\jdk8\bin \java
n1=10
jack
jack
2109957412 2109957412
可以看到 hashCode 值一样,说明 scala 与 Java 机制一样,如果只是改了名字就创建了一个新的对象是不可能的,只有在 teiger 中 new 新创建一个 tiger 对象进行返回才不是同一个对象。
3、细节3
Scala 中的函数可以根据函数体最后一行代码自行推断函数返回值类型。那么在这种情况下,return 关键字可以省略
案例演示:
def getSum(n1: Int, n2: Int): Int = {
n1+n2
//return n1+n2,return 可写可不写
}
4、细节4
因为 Scala 可以自行推断,所以在省略 return 关键字的场合,返回值类型也可以省略
案例演示:
def getSum(n1: Int, n2: Int): Int = {
//也可以写成(n1: Int, n2: Int) = {(:Int 是一个整体),这种写法使用较多,因为 Scala 可以自行推断
n1+n2
}
5、细节5
如果函数明确使用 return 关键字, 那么函数返回就不能使用自行推断了,就是自行推断无效,这时要明确写成:返回类型=,否则就会报错,当然如果什么都不写,即 return 返回值为()
(1)案例演示:
package com . atguigu . chapter05. fundetails
object Details01 {
def main(args: Array[string]): Unit = {
println
(getSum2(10,30))
//传入值10,30,按理说返回应该为40,但实际返回为( )
}
def getSum(n1: Int, n2: Int): Int = {
//因为这里有明确的 return,这时getSum就不能省略 Int =的 Int 了,即如果写了 return ,返回值类型就不能省略
retur
n
n1 + n2
}
/*
def getSum(n1: Int, n2: Int) = {
//这样写会出现报错,如果函数明确使用 return 关键字, 那么就是自行推断无效,这时要明确写成:返回类型=,即要写出,如上形式。
retur
n
n1 + n2
}
*
/
def getSum(n1: Int, n2: Int) {
//当然如果什么都不写,即表示该函数没有返回值,代表不用类型推断,即使写了 return 也无效。
retur
n
n1 + n2
}
}
(2)运行结果:
D: \program\jdk8\bin \java
()
二、细节6-10
6、细节6
如果函数明确声明无返回值(声明 Unit),那么函数体中即使使用 return 关键字也不会有返回值
案例演示:
…
p
rintln(getSum3(9,9))
//仍然返回值为()
def getSum(n1: Int, n2: Int): U
nit
= {
// n1+n2返回值应该为 Int,但这里为 Unit 是因为编译器认为程序没有返回值,只要接收就行。这种写法其实与上面的不写返回值等价
retur
n
n1 + n2
}
运行结果如下:
D: \program\jdk8\bin \java
()
()
7、细节7
如果明确函数无返回值或不确定返回值类型,那么返回值类型可以省略(或声明为 Any)
案例演示:
def f3(s: String)={
//有一个函数 f3,传入 s
if(s.length >= 3)
//如果长度大于3
s+ "123" //则返回 s+ "123"
else
3} //否则返回3
而 s+ "123"为 String 字符型,返回3为 int 类型,显然 s 的数据类型写什么都不合适,所以这里有两种选择,第一种为省略,使用类型推导,第二种为声明为 Any,如下
def f4(s: String): Any = {
//因为 Any 是所有类的父类
if(s.length >= 3)
s
+ "123"
else
3}
8、细节8
Scala 语法中任何的语法结构都可以嵌套其他语法结构(灵活),即:函数中可以再声明/
定义函数,类中可以再声明类,方法中可以再声明/定义方法
(1)案例演示:
package com. atguigu. chaptero5. fundetails
object Details03 {
def main(args: Array[String]): Unit = {
def f1(): Unit = {
//在主函数中写入 f1函数,而f1函数并不是内部函数,地位与 sayOk 一样,只是在底层会写成私有类 private final 函数
println("f1")
}
println("ok~~")
//此时进行运行输出并不会输出 f1字符串,因为是定义的,所以输出为 ok~~
def sayOk(): Unit = {
// 这里也是一个成员,而为了区分成员的区别,会在后台写为 private final sayOk$1 (),自动加上$进行区分
println("main sayOk")
//输出主函数的 sayOk,相当于 object 的方法,但这里并没有看成函数所以也无所谓
def sayOk(): Unit = {
//在 def sayOk()中定义 def sayOk()仍然是放在 Details03中,只是名字变为了 private final sayok$2 ()
println("sayok sayok" )
}
}}
def sayOk(): Unit ={
//成员。如果再在主函数中写一个同名的 sayOk 也是不会报错的,甚至还可以在 sayOk中嵌套 sayOk,如上。
println("main sayOk" )
}
而关于f1是否是内部函数还是和 sayok()同地位的函数可以打开相应的 class 文件进行查看,案例中打开Details03$.class 文件进行查看,可以看到 f1仍然是对应类的方法,只是 f1是私有的 final 方法,不能被更改也不能被继承下去,如:
…
public final class Detail303$
{
public static final MODULE$;
static
new () ;
}
public void main (String[] arga)
Predef. . MODULE$ . print1n("ok--");
}
public void sayOk ()
Predef. .MODULE$.print1n("main sayOk");
}
private final void f1$1 ()
Predef. .MODULE$. println("f1");
}
…
(2)此时运行上述代码:
D: \program\jdk8\bin \java
ok
~~
运行完成后打开编译工具进行查看,如下:
…
private final void sayOk$2 ()
Predef. . MODULE$ . print
l
n("sayok sayok");
private final void sayOk$1 ()
Predef. . MODULE$ . print
l
n("main sayOk");
…
说明并不是在主函数内部定义的函数就是内部函数,并且地位和 sayOk 一样,只是修饰符发生了一些变化而已。
9、细节9
Scala 函数的形参,在声明参数时,可以直接赋初始值(默认值),这时调用函数时,如果没有指定实参,则会使用默认值。如果指定了实参,则实参会覆盖默认值。
案例演示:
package com. atguigu. chapter05. fundetails
object Details04 {
def main(args: Array[String]): Unit = {
//函数 main,传入 args,
println(sayOk("mary"))
//如果不传值的话调用的结果应该是默认值 jack,如果此时运行结果为 jack ok!而如果传入值"mary",那么就会把默认值覆盖掉,所以运行结果为 mary ok!
}
//name.形参的默认值 jack
def sayok(name : String = "jack"): string= {
return name +"ok! "
}
}
10、细节10
如果函数存在多个参数,每一个参数都可以设定默认值,那么这个时候,传递的参数到底是覆盖默认值,还是赋值给没有默认值的参数,就不确定了(默认按照声明顺序[从左到右])。在这种情况下,可以采用带名参数[案例演示+练习]
(1)案例演示:
package com . atguigu. chapter05. fundetails
object DetailParameter05 {
def main(args: Array[String]): Unit = {
mysqlCon()
}
def mysqlCon(add: String = "localhost",port :Int = 3306,
//mysqlCon 函数,里面有本地地址add,port 默认为3306
user
:
String = "
ro
ot", pwd : String = "
ro
ot"): Unit = {
//用户名密码都为 root
println("add=" + add)
println("port=" + port)
printIn("user=" + user)
println("pwd=" + pwd)
}
}
此时什么都不传入,则输出值全部采用默认值,运行结果为:
D: \program\jdk8\bin \java
add=localhost
port=3306
user= root
pwd=root
那么如果修改代码为:
…
def main(args: Array[string]): Unit = {
mysqlcon()
mysqlCon("127.0.0.1",7777)
//传入"127.0.0.1",7777,并且遵守从左到右覆盖,所以此时 add 被127.0.0.1覆盖,port 被7777覆盖
}
…
运行结果为:
D: \program\jdk8\bin \java
add=127.0.0.1
port=7777
user= root
pwd=root
那么如果希望指定覆盖某个默认值,则使用带名参数即可,比如修改用户名和密码,修改代码为:
…
def main(args: Array[string]): Unit = {
//mysqlcon()
//mysqlCon("127.0.0.1",7777)
mysqlcon(user ="tom"
,
pwd ="123" )
//如果直接写 mysqlcon("tom","123" )是不行的,因为默认是从左到右覆盖,tom 还可以与 String 进行匹配,但是123与 Int 类型是不能匹配的,所以可以写带名参数,就是写入参数名跟上等号
…
运行结果为:
D: \program\jdk8\bin \java
add=localhost
port=3306
user=tom
pwd=123
(2)练习
观察如下代码,分析程序输出结果:
def f6( p1 :String= "v1"
,
p2 : String ) {
println(p1 + p2);
}
f6("v2") :分析为 p1+p2输出应该为 v2,而因为 p2没有默认值,v2应该按照从左到右覆盖,v2则把 v1覆盖掉,但是p2没有给值,所以会报错。
f6(p2="v2"):这里使用了代名参数,所以输出成功,为 v2。
下面进行代码验证:
package com . atguigu. chapter05. fundetails
object DetailParameter05 {
def main(args: Array[String]): Unit = {
//f6("v2") 这里直接出现报错,报错为没有指定 p2的值,只有指定了 p2的值才行,比如("v2",”v3”),指定 v3
f6(p2="v2")
}