add scala

pull/1/head
wanglei 2020-08-09 19:32:57 +08:00
parent e579248efa
commit 445dbbe270
10 changed files with 1069 additions and 0 deletions

View File

@ -0,0 +1,30 @@
## 1.java中传递变长参数
在java中传递变长参数的方式为`...`,看个简单的实例
```
public static void argsTest(String... args) {
for(String each: args) {
System.out.println(each);
}
}
public static void main(String[] args) {
String s1 = "a", s2 = "b", s3 = "c";
argsTest(s1, s2, s3);
}
```
## 2.scala中传递变长参数
scala中也支持传递变长参数而且比java中更简单只需要在参数类型后面使用特殊符号"*"即可。同样看一个简单的例子
```
def findMax(values: Int*) = {
values.foldLeft(values(0)) {
(x, y) => Math.max(x, y)
}
}
def main(args: Array[String]): Unit = {
println(findMax(1, 3, 5, 2, 4, 6))
}
```

View File

@ -0,0 +1,134 @@
项目中经常有发送http请求的需求现在将java,python,scala中发送http请求的方法稍作记录以备不时之需。
## 1.java版本
java代码相对来说最为冗长。。。这也是java的一贯风格。。。
```
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
public class HttpUtils {
protected static Logger LOGGER = LoggerFactory.getLogger(HttpUtils.class);
public static HttpClient httpClient = new DefaultHttpClient();
public static String getResponse(String url, int retryTimes) {
for(int i = 0; i < retryTimes; i++) {
LOGGER.info("get response with the request: {}", url);
try {
HttpResponse response = httpClient.execute(new HttpGet(url));
HttpEntity entity = response.getEntity();
if(response.getStatusLine().getStatusCode() == 200) {
String res = genResponseContext(entity.getContent());
if(StringUtils.isNotBlank(res)) return res;
}
} catch (Exception ex) {
System.out.println("get response with error");
ex.printStackTrace();
LOGGER.error("get response with error: \n", ex);
try {
Thread.sleep(1000 * (i + 1));
} catch (InterruptedException interEx) {
interEx.printStackTrace();
}
}
}
return StringUtils.EMPTY;
}
public static String genResponseContext(InputStream is) {
BufferedReader reader = null;
try {
StringBuilder sb = new StringBuilder();
reader = new BufferedReader(new InputStreamReader(is));
String line;
while( (line = reader.readLine()) != null) {
sb.append(line).append("\n");
}
return sb.toString().trim();
} catch (Exception ex) {
LOGGER.error("get response error: {} \n", ex);
return null;
} finally {
if (reader != null) {
try {
reader.close();
} catch (Exception e) {
}
}
}
}
public static void main(String[] args) {
String url = "http://www.baidu.com";
String result = getResponse(url, 2);
System.out.println(result);
}
}
```
## 2.python版本
python代码相对来说就简单很多了。。。
```
#!/usr/bin/env python
# -*- coding: utf-8
import urllib2
def send_http_request(url):
request = urllib2.Request(url)
response = urllib2.urlopen(request)
data = response.read()
print data
send_http_request("http://www.baidu.com")
```
## 3.scala版本
scala也是JVM系所以跟java版本有一点像但简洁很多。
```
import org.apache.http.client.methods.HttpGet
import org.apache.http.impl.client.DefaultHttpClient
import scala.io.Source
object HttpUtils {
def send_http_request(url: String): Unit = {
val httpclient = new DefaultHttpClient()
try {
val response = httpclient.execute(new HttpGet(url))
val entity = response.getEntity
val result = Source.fromInputStream(entity.getContent).getLines().mkString("\n")
println(result)
} catch {
case ex: Exception => ex.printStackTrace()
}
}
def main(args: Array[String]): Unit = {
val url = "http://www.baidu.com"
send_http_request(url)
}
}
```

View File

@ -0,0 +1,158 @@
## 1.object
object的特点是
1.可以拥有属性和方法,且默认都是"static"类型可以直接用object名直接调用属性和方法不需要通过new出来的对象也不支持
2.object里的main函数式应用程序的入口。
3.object和class有很多和class相同的地方可以extends父类或Trait但object不可以extends object即object无法作为父类。
## 2.class
一个主构造器(函数),其他是辅助构造器
辅助构造器的实现体里,必须引用(调用)主构造器
主构造器的参数,也会成为类的属性
辅助构造函数的名称都是this
辅助构造函数中必须以一个其他辅助构造器或主构造器的调用开始。
构造函数
生成的字段方法
name:String 对象私有字段。如果没有方法使用name,则没有该字段。
Private val/var name String 私有字段私有getter/setter方法。
Val/var name:String 私有字段公有getter/setter方法
scala类中是没有static方法的那么如何实现某个类既有普通方法又有静态方法伴生对象就可以满足这个要求伴生类和伴生对象可以相互访问彼此的私有成员。
```
class TestClass(val id: Int) {
def this(id1: Int, id2: Int) {
this(id1)
}
def add2(a: Int, b: Int) = {
a + b
}
}
object TestClass {
def apply(id: Int)= {
println("-----------apply--------")
new TestClass(id)
}
def add(a: Int, b: Int): Int = {
a + b
}
def main(args: Array[String]): Unit = {
val r1 = TestClass.add(1, 2)
println(s"r1 is: $r1")
val t1 = new TestClass(1)
val r2 = t1.add2(3, 4)
println(s"r2 is: $r2")
val t2 = TestClass(100000)
println(s"t2.id is: ${t2.id}")
val t3 = new TestClass(100, 200)
println((s"t3.id is: ${t3.id}"))
}
}
```
输出结果为:
```
r1 is: 3
r2 is: 7
-----------apply--------
t2.id is: 100000
t3.id is: 100
```
## 3.trait
Scala的Trait相当于Java里的Interface但Trait不仅可以定义函数还可以有函数体实现。实现关键词是extends实现多个Trait用with。当extends的多个Trait里有相同函数时子类必须重写该函数。
父trait里无函数体的函数子类必须override
重写父类里有函数体的函数必须有关键词override
trait里的变量都是val类型
在trait里定义的的变量必须是val类型如果变量没初始化子类必须override
```
trait TestTrait {
def printfun()
}
class TestTraitImp extends TestTrait {
override def printfun(): Unit = {
println("I'm TestTrait implement!")
}
}
object TestClass {
def main(args: Array[String]): Unit = {
val t4 = new TestTraitImp()
t4.printfun()
}
}
```
运行的结果为
```
I'm TestTrait implement!
```
## 4.scala类层级结构
Scala里每个类都继承自通用的名为Any的超类。因为所有的类都是Any的子类所以定义在Any中的方法就是“共同的”方法它们可以被任何对象调用。
Any有两个子类AnyVal和AnyRef(相当于Java里的Object)。
AnyVal是Scala里每个内建值类的父类。有9个这样的值类Byte、Short、Char、Int、Long、Float、Double、Boolean和Unit。其中的前8个都对应到Java的基本类型。这些值类都被定义为既是抽象的又是final的不能使用new创造这些类的实例。Unit被用作不返回任何结果的方法的结果类型。Unit只有一个实例值写成()。
AnyRef类是Scala里所有引用类(reference class)的基类。它其实是Java平台上java.lang.Object类的别名。因此Java里写的类和Scala里写的都继承自AnyRef。
scala.Null和scala.Nothing是用统一的方式处理Scala面向对象类型系统的某些“边界情况”的特殊类型。Null类是null引用对象的类型它是每个引用类继承自AnyRef的类的子类。Null不兼容值类型。Nothing类型在Scala的类层级的最低端它是任何其他类型的子类型。然而根本没有这个类型的任何值。Nothing的一个用处是它标明了不正常的终止。
## 5.scala实现单例模式
因为scala中没有static所以我们用伴生对象来实现单例模式。
```
class SingleModel {
def printfunc() = {
println("this is SingleModel!")
}
}
object SingleModel {
private var instance: SingleModel = null
def getInstance() = {
if (instance == null) {
this.synchronized {
if (instance == null) {
instance = new SingleModel()
}
}
}
instance
}
}
```
客户端调用的代码如下
```
object SingleModelClient {
def main(args: Array[String]): Unit = {
val instance = SingleModel.getInstance()
instance.printfunc()
}
}
```
最后的输出为
```
this is SingleModel!
```

View File

@ -0,0 +1,138 @@
scala的集合中提供了三种排序的方式sorted,sortWith,sortBy。那么这三种方式有什么不同呢下面我们结合源码来分析一下
## 1.sorted
先来看看scala中sorted的源码。
```
def sorted[B >: A](implicit ord: Ordering[B]): Repr = {
val len = this.length
val arr = new ArraySeq[A](len)
var i = 0
for (x <- this.seq) {
arr(i) = x
i += 1
}
java.util.Arrays.sort(arr.array, ord.asInstanceOf[Ordering[Object]])
val b = newBuilder
b.sizeHint(len)
for (x <- arr) b += x
b.result
}
```
源码中有两点值得注意的地方:
1.sorted方法中有个隐式参数ord: Ordering。
2.sorted方法真正排序的逻辑是调用的java.util.Arrays.sort。
## 2.sortBy
看看sortBy的源码很简单。
```
def sortBy[B](f: A => B)(implicit ord: Ordering[B]): Repr = sorted(ord on f)
```
sortBy最后也是调用的sorted方法。不一样的地方在于sortBy前面还需要提供一个属性。
## 3.sortWith
sortWith的源码如下。
```
def sortWith(lt: (A, A) => Boolean): Repr = sorted(Ordering fromLessThan lt)
```
跟前面两个不同的是sortWith需要传入一个比较函数用来比较
## 4.实例
理论部分说完了,下面来干货
```
object ImplicitValue {
implicit val KeyOrdering = new Ordering[String] {
override def compare(x: String, y: String) : Int = {
y.compareTo(x)
}
}
}
```
首先定义了一个隐式比较器。
```
def test1() = {
import ImplicitValue.KeyOrdering
val list = List( "a", "g", "F", "B", "c")
val sortedList = list.sorted
println(sortedList) // List(g, c, a, F, B)
}
```
注意因为我们将隐式比较器import了进来这个时候sorted排序的规则是按照我们自定义的比较器进行比较。在我们自定义的比较器中定义的是按字符串逆序所以最终的输出结果为字符串按从大到小的顺序排列
再来看看sortWith的用法。
```
//忽略大小写排序
def compareIngoreUpperCase(e1: String, e2: String) : Boolean = {
e1.toLowerCase < e2.toLowerCase
}
def test2() = {
val list = List( "a", "g", "F", "B", "c")
val sortWithList1 = list.sortWith(_ < _) // List(B, F, a, c, g)
val sortwithList2 = list.sortWith((left, right) => left < right) //List(B, F, a, c, g)
val sortwithList3 = list.sortWith(compareIngoreUpperCase) // List(a, B, c, F, g)
println(sortWithList1)
println(sortwithList2)
println(sortwithList3)
}
```
本例中, sortWithList1与sortWithList2最终的结果是一致的只不过写法不一样而已都是按字符串从小到大的顺序排列。sortwithList3则是按照传入的compareIngoreUpperCase函数进行排序
最后看看sortBy的代码
```
def test3() = {
val m = Map(
-2 -> 5,
2 -> 6,
5 -> 9,
1 -> 2,
0 -> -16,
-1 -> -4
)
//按key排序
m.toList.sorted.foreach{
case (key, value) =>
println(key + ":" + value)
}
println
//按value排序
m.toList.sortBy(_._2).foreach {
case (key, value) =>
println(key + ":" + value)
}
}
```
最后的输出结果为:
```
-2:5
-1:-4
0:-16
1:2
2:6
5:9
0:-16
-1:-4
1:2
-2:5
2:6
5:9
```

View File

@ -0,0 +1,100 @@
在别的编码语言中break与continue两种控制语句是非常常见的用法一般也有对应的关键字。但是在scala中没有专门的break与continue关键字。那怎么在循环中实现break与continue功能呢
## 1.实现break功能
```
package com.xiaomi.leilei.test1
import scala.util.control.Breaks._
/**
* Created by wanglei on 17/8/11.
*/
object breakdemo {
def breaktest() = {
val arr = Array(1, 2, 3, 4, 5, 6)
breakable {
arr.foreach { x =>
if (x > 3) break
else println(x)
}
}
}
def main(args: Array[String]): Unit = {
breaktest()
}
}
```
最后的输出结果为:
```
1
2
3
```
## 2.实现continue功能
```
package com.xiaomi.leilei.test1
import scala.util.control.Breaks._
/**
* Created by wanglei on 17/8/11.
*/
object breakdemo {
def continuetest() = {
val arr = Array(1, 2, 3, 4, 5, 6)
arr.foreach {
x => breakable {
if(x == 4) break()
else println(x)
}
}
}
def main(args: Array[String]): Unit = {
continuetest()
}
}
```
通过上面的代码不难发现实现break与continue功能都需要`util.control.Breaks._`类的辅助。不同的是将整个循环的逻辑放在breakable方法中在需要真正跳出循环的时候使用break方法这样达到了跳出整个循环的目的。而continue功能是将breakable放在循环内这样可以实现结束本次循环的目的而不是结束整个循环。
## 3.Breaks的部分源码
```
package scala
package util.control
/** A class that can be instantiated for the break control abstraction.
* Example usage:
* {{{
* val mybreaks = new Breaks
* import mybreaks.{break, breakable}
*
* breakable {
* for (...) {
* if (...) break()
* }
* }
* }}}
* Calls to break from one instantiation of `Breaks` will never
* target breakable objects of some other instantiation.
*/
```
如果翻译过来就是说:
1.Breaks是一个可以实例化为中断控制抽象的类。
2.Breaks的实例对象永远不用破坏要被中断对象的实例。
3.注释中给出了明确的要实现break功能的代码样式。

View File

@ -0,0 +1,109 @@
## 1.apply方法
当scala中类或者对象有一个主要用途的时候apply方法就是一个很好地语法糖。
请看下面一个简单的例子:
```
class Foo(foo: String) {
}
object Foo {
def apply(foo: String) : Foo = {
new Foo(foo)
}
}
```
定义了一个Foo类并且在这个类中有一个伴生对象Foo里面定义了apply方法。有了这个apply方法以后我们在调用这个Foo类的时候用函数的方式来调用
```
object Client {
def main(args: Array[String]): Unit = {
val foo = Foo("Hello")
}
}
```
我们用`Foo("Hello")`的方式就得到了一个Foo类型的对象这一切就是apply方法的功劳。如果没有apply方法我们将需要使用new关键字来得到Foo对象。
## 2.apply方法用来做工厂
apply方法的最佳实践方式之一就是用来做工厂。比如在Scala的标准库中许多集合类给我们提供了apply方法来创建集合
```
object Client {
def main(args: Array[String]): Unit = {
val arr = new Array[Int](3)
arr(0) = 0
arr(1) = 1
arr(2) = 2
arr.foreach(x => print(x + " "))
println()
val array = Array(1,2,3)
array.foreach(x => print(x + " "))
}
}
```
上面两种方式我们都可以用来创建Array。第一种方式是使用new关键字这是传统的面向对象的方式。那么第二种方式是什么情况呢如果我们在IDE里点进去可以发现IDE会提示我们有一个apply方法。点进去看apply方法的源码
```
/** Creates an array of `Int` objects */
// Subject to a compiler optimization in Cleanup, see above.
def apply(x: Int, xs: Int*): Array[Int] = {
val array = new Array[Int](xs.length + 1)
array(0) = x
var i = 1
for (x <- xs.iterator) { array(i) = x; i += 1 }
array
}
```
## 3.unapply方法
从上面的例子不难看出apply方法有点类似于java中的构造函数接受构造参数变成一个对象。那么unapply方法就刚好相反他是接受一个对象从对象中提取出相应的值。
unapply方法主要用于模式匹配中。
看个简单的例子:
```
class Money(val value: Double, val country: String) {}
object Money {
def apply(value: Double, country: String) : Money = new Money(value, country)
def unapply(money: Money): Option[(Double, String)] = {
if(money == null) {
None
} else {
Some(money.value, money.country)
}
}
}
```
客户端实现:
```
def testUnapply() = {
val money = Money(10.1, "RMB")
money match {
case Money(num, "RMB") => println("RMB: " + num)
case _ => println("Not RMB!")
}
}
```
最后输出为:
```
RMB: 10.1
```
所以下面那种创建数组的方式其实是通过Array类的apply方法实现的。
## 参考文档:
1.https://stackoverflow.com/questions/9737352/what-is-the-apply-function-in-scala/9738862#9738862
2.https://twitter.github.io/scala_school/zh_cn/basics2.html

View File

@ -0,0 +1,88 @@
## 1.scala枚举原理
严格来说和其它语言不同Scala 并没有枚举这种类型。Scala 中的枚举值只是Enumeration下的Value类的实例而不是枚举自身的实例。Value类的实例主要依靠其构造方法的两个值:id与name来构建。其中id是自增长name如果不指定时默认使用的就是值的名字。所以不像其它语言Scala 并不能任意定义构造方法来构造枚举。
```
abstract class Enumeration (initial: Int) extends Serializable {
thisenum =>
def this() = this(0)
...
protected final def Value(i: Int, name: String): Value = new Val(i, name)
...
}
abstract class Value extends Ordered[Value] with Serializable {
/** the id and bit location of this enumeration value */
def id: Int
/** a marker so we can tell whose values belong to whom come reflective-naming time */
private[Enumeration] val outerEnum = thisenum
override def compare(that: Value): Int =
if (this.id < that.id) -1
else if (this.id == that.id) 0
else 1
override def equals(other: Any) = other match {
case that: Enumeration#Value => (outerEnum eq that.outerEnum) && (id == that.id)
case _ => false
}
override def hashCode: Int = id.##
/** Create a ValueSet which contains this value and another one */
def + (v: Value) = ValueSet(this, v)
}
/** A class implementing the [[scala.Enumeration.Value]] type. This class
* can be overridden to change the enumeration's naming and integer
* identification behaviour.
*/
@SerialVersionUID(0 - 3501153230598116017L)
protected class Val(i: Int, name: String) extends Value with Serializable {
def this(i: Int) = this(i, nextNameOrNull)
def this(name: String) = this(nextId, name)
def this() = this(nextId)
...
}
```
从上面的源码可以看出,真正的主构造方法为`Val(i: Int, name: String)`下面的辅助构造方法有三个分别可以只传id只传name或者任何参数不传。
## 2.例子
以一个星期几的枚举为例看看scala中的枚举如何使用。
```
object Weekday extends Enumeration {
type Weekday = Value
val Monday = Value(1)
val Tuesday = Value(2, "tue")
val Wednesday = Value(3)
val Thursday = Value(4)
val Friday = Value(5)
val Saturday = Value(6)
val Sunday = Value(7)
val SS = Value
def main(args: Array[String]): Unit = {
// 枚举值,Monday只传了id,name默认为枚举值
val monday = Weekday.Monday
println(monday)
// 通过name获得枚举值
val tuesday = Weekday.withName("tue")
println(tuesday)
// 通过id获得枚举值
println(Weekday(6))
// 获得id
println(Weekday.SS.id)
}
}
```
输出的结果为
```
Monday
tue
Saturday
8
```

View File

@ -0,0 +1,57 @@
## 1.什么是柯里化函数
在scala相关的教程与参考文档里经常会看到柯里化函数这个词。但是对于具体什么是柯里化函数柯里化函数又有什么作用其实可能很多同学都会有些疑惑。今天就跟大家来掰扯掰扯柯里化函数(Haskell Curry)。
首先看两个简单的函数:
```
def add(x: Int, y: Int) = x + y
def addCurry(x: Int)(y: Int) = x + y
```
以上两个函数实现的都是两个整数相加的功能。对于add方法调用方式为`add(1,2)`。对于addCurry方法调用的方式为`addCurry(1)(2)`。这种方式就叫做柯里化。说得更加简单粗暴一点,有多个参数列表,或者说多个小括号括起来的函数参数列表的函数就是柯里化函数。
## 2.为什么要使用柯里化
看了前面的例子,很多人估计忍不住就要开喷了:你特么逗我呢?好好的给一个方法传两个参数不挺好么,干嘛搞这么复杂。
我能理解大家的感受,我第一次看到这种做法心里也是有一万匹草泥马奔腾而过。不过我们要相信一句话,存在即合理,既然这么干,肯定是有合理的地方,暂且慢慢分析。
curry化最大的意义在于把多个参数的函数等价转化成多个单参数函数的级联这样所有的函数就都统一了方便做lambda演算。 在scala里curry化对类型推演也有帮助scala的类型推演是局部的在同一个参数列表中后面的参数不能借助前面的参数类型进行推演curry化以后放在两个参数列表里后面一个参数列表里的参数可以借助前面一个参数列表里的参数类型进行推演。
上面的说法比较书面化,用更加口语化的一点来描述:
1.把多个参数转化为单参数函数的级联,达到了动态确定参数的目的。
2.当某些参数不确定时,可以先保留一个存根。剩余的参数确定以后,就可以通过存根调用剩下的参数。
3.通过类似于建造者模式(building),把一个大的东西的构造过程,切成一个个的小模块来逐步构造。举个最简单的例子,`Person.name("xxx").age(num).salary(count).phone(xxxx)`。
## 3.scala源码中的柯里化
scala源码中存在大量的柯里化函数的应用看几个简单的例子。
```
def foldLeft[B](z: B)(f: (B, A) => B): B = {
var acc = z
var these = this
while (!these.isEmpty) {
acc = f(acc, these.head)
these = these.tail
}
acc
}
```
从foldLeft的方法原型里很容易就看出来这是就是典型的柯里化函数的应用。foldLeft有两个参数一个为参入的初始值z类型为B。另一个为函数ff有两个参数一个类型与传入的初始值相同另一个为集合本身的类型A最后方法返回的类型为B。
```
def foldtest() = {
val list = List(1, 2, 3)
val strResleft = list.foldLeft("res:")((x: String, y:Int) => x + y)
val strResRight = list.foldRight("res:")((y: Int, x: String) => x + y)
println(strResleft)
println(strResRight)
}
```
函数的输出:
```
res:123
res:321
```

View File

@ -0,0 +1,147 @@
在前面的文章里我们讲了在java中如何利用泛型实现数值类型加法。具体可以参考博文 http://blog.csdn.net/bitcarmanlee/article/details/78733637。
那么在scala中我们怎么实现上面的需求呢
## 1.用 <: extends
如果按照在java中的处理思路我们可以这么尝试一下
```
def numberAdd[T <: Number](t1 : T, t2 : T) = {
t1.doubleValue() + t2.doubleValue()
}
@Test
def numberaddtest() = {
val (t1, t2) = (1, 2)
numberAdd(t1, t2)
}
```
在scala中`<:`javaextendstestrunIDE
```
Error:(26, 19) type mismatch;
found : Int
required: T
numberAdd(t1, t2)
Error:(26, 9) inferred type arguments [Int] do not conform to method numberAdd's type parameter bounds [T <: Number]
numberAdd(t1, t2)
Error:(26, 23) type mismatch;
found : Int
required: T
numberAdd(t1, t2)
```
很明显,上面的方法是行不通的。
## 2.使用Numeric[T]
为什么上面的方法行不通呢因为Scala的数字类型并不都共享一个超类所以我们不能使用T <: Number使ScalamathT Numeric[T]使
首先上可以运行的代码:
```
def add[T](x: T, y: T)(implicit num: Numeric[T]) = {
val result = num.plus(x, y)
result
}
@Test
def testAdd() = {
val int1 = 1
val int2 = 2
println("int sum is: " + add(int1, int2))
val long1 = 100L
val long2 = 200L
println("long sum is: " + add(long1, long2))
val f1 = 1.0f
val f2 = 2.0f
println("float sum is: " + add(f1, f2))
val d1 = 1.0
val d2 = 2.0
println("double sum is: " + add(d1, d2))
}
```
将上面的test方法run起来可以得到如下输出
```
int sum is: 3
long sum is: 300
float sum is: 3.0
double sum is: 3.0
```
## 3.Numeric[T]的用法
Numeric[T]在scala中源码如下
```
type Numeric[T] = scala.math.Numeric[T]
val Numeric = scala.math.Numeric
```
当然我们也可以通过implicitly方法用context bound(上下文绑定)的方式让上面的代码更简单:
```
def add2[T: Numeric](x: T, y: T) = {
implicitly[Numeric[T]].plus(x, y)
}
```
其中implicitly方法在scala中的定义如下
```
@inline def implicitly[T](implicit e: T) = e // for summoning implicit values from the nether world
```
implicitly 主要是在当前作用域查找指定类型,例如以下的例子:
```
@Test
def testimplicit() = {
implicit val x = 1
implicit val x1 = 2.0
val y = implicitly[Int]
val z = implicitly[Double]
println(y + "\t" + z)
}
```
将test方法run起来以后输出如下
```
1 2.0
```
## 4.数值集合求和
搞定了单个的数值求和,那么数值集合的求和自然就变得容易了:
```
def add4[T: Numeric](x : Array[T]) = {
var sum = 0.0
for(each <- x) {
sum += implicitly[Numeric[T]].toDouble(each)
}
println(sum)
}
@Test
def test() = {
val array = Array(1, 2, 3)
add4(array)
}
```
代码输出:
```
6.0
```
## 参考内容:
1.https://stackoverflow.com/questions/4373070/how-do-i-get-an-instance-of-the-type-class-associated-with-a-context-bound
2.https://stackoverflow.com/questions/2982276/what-is-a-context-bound-in-scala
3.http://www.jianshu.com/p/1d119c937015 Scala中的Implicit详解
4.https://vimsky.com/article/1562.html scala常见问题整理
5.https://fangjian0423.github.io/2015/06/07/scala-generic/ scala泛型

View File

@ -0,0 +1,108 @@
## 1.简介
scopt是github上有人开发的一个用来解析参数的轻量级小工具对于日常解析参数的场景基本够用项目中使用也比较多现在稍微记录一下用法。
## 2.依赖
在pom.xml文件中加入以下依赖
```
<dependency>
<groupId>com.github.scopt</groupId>
<artifactId>scopt_2.10</artifactId>
<version>3.2.0</version>
</dependency>
```
## 3.例子
直接上代码先
```
object ScoptTest {
case class Params(
input: String = "input",
output: String = "output",
index: Int = 0,
partitionnum: Int = 2,
debug: Boolean = false)
val parser = new scopt.OptionParser[Params]("test") {
head("this is a test for scopt params")
opt[String]('i', "input").required().action {
(x, c) => c.copy(input = x)
}.text("default of input is input")
opt[String]('o', "output").required().action {
(x, c) => c.copy(output = x)
}.text("default of output is output")
opt[Int]("index").optional().action {
(x, c) => c.copy(index = x)
}.text("default of index is 0")
opt[Int]("partitionnum").optional().action {
(x, c) => c.copy(partitionnum = x)
}.text("default of partitionnum is 2")
opt[Boolean]("debug").optional().action {
(x, c) => c.copy(debug = x)
}.text("default of debug is false")
}
def init(args: Array[String]) = {
val params = parser.parse(args, Params()).get
println(params.input)
println(params.output)
println(params.index)
println(params.partitionnum)
println(params.debug)
}
def main(args: Array[String]): Unit = {
init(args)
}
}
```
首先我们先写一个case class里面加入各种我们需要的参数。
然后生成一个parser对象解析传入的参数即可。
将上面的代码run起来如果不传入input与output这两必须参数会有如下提示
```
Error: Missing option --input
Error: Missing option --output
this is a test for scopt params
Usage: test [options]
-i <value> | --input <value>
default of input is input
-o <value> | --output <value>
default of output is output
--index <value>
default of index is 0
--partitionnum <value>
default of partitionnum is 2
--debug <value>
default of debug is false
```
传入如下参数
```
-i input1 -o output2
```
代码运行的结果为
```
input1
output2
0
2
false
```