HashMap 是散列表实现的键值对容器,本文分析其源码和实现原理。
HashMap 是散列表实现的键值对容器,本文分析其源码和实现原理。
TreeMap 是红黑树实现的键值对容器,本文分析其源码和实现原理。
LinkedList 是有序双向链表,本文分析其源码和实现原理。
ArrayList 是最常见的 List 实现,本文分析其源码和实现原理。
还用问么,ViewPager 以及 Fragment 呀,非常简单。
下面的 API 可以根据初始颜色和结束颜色计算中间值。
1 | Object ArgbEvaluator.evaluate(float fraction, Object startValue, Object endValue); |
1 |
|
1 | public class MainActivity extends AppCompatActivity implements ViewPager.OnPageChangeListener { |
其实实现方式并不复杂,监听 ViewPager 的滚动然后计算中间值即可,重要的是又学习到酷炫的新东西了。
由于三体人对我们的科技封锁,我们无法在 Android 开发中启用 Java 1.8 的重要特性——Lambda 表达式。但现在我们可以通过一些工具启用它,然后使用 Lambda 表达式替换没有什么实际意义的单方法匿名内部类。
可能有些人还不太清楚到底什么是 Lambda 表达式,这里先对 Lambda 表达式进行一个简单的介绍。
Lambda 表达式是函数式编程语言的特性,它简单的说就是一个匿名函数。
我们先看一个 Groovy 的例子:
1 | [1, 2, 3, 4, 5].asList().forEach { x -> println x } |
在这个例子中,我们使用foreach
来遍历一个List
并打印每一个值。我们传入了一个 Lambda 表达式:{ x -> println x }
,这个表达式就是我们对每一个值进行的操作,在本例中就是打印它们。
在这里 Lambda 表达式是一个映射函数,foreach
接受了它作为参数,然后对List
中的每一个值进行遍历。
在函数式编程语言中,函数是一等公民(first class)。它们也可以作为变量或者参数被传递而且它们也是一个类。
但在 Java 中函数并不是一等公民,如果我们需要传递一个方法,必须要有一个对象包含这个方法,然后把这个对象传递过去。
所以我们经常会见到类似这样的代码:
1 | textView.setOnClickListener(new View.OnClickListener() { |
但是实际上,我们需要的只是onClick
这个方法里面的内容,其它的部分(new OnClickListener)实在是没有什么实际的意义,只是一个必须的语法而已。
所以 Java 1.8 也引入了部分函数式编程的特性——Lambda 表达式。
如果使用 Lambda 表达式,上面那个例子可以被简化为这样
1 | textView.setOnClickListener(v -> { |
如果只有一行代码我们还可以省略大括号
1 | textView.setOnClickListener(v -> doSomething()); |
当啷啷~ 是不是省略了很多代码,有没有很爽的感觉。
有了 Lambda 表达式,从此我们的代码可以清爽简洁,而且看起来也很好理解:箭头的左边是形参,右边是函数体,整个 Lambda 表达式就是一个函数(就是数学中的函数)。
更多 Lambda 表达式的信息可以查看《Java 8新特性:lambda表达式》——廖雪峰。
安利了这么多 Lambda 表达式的优点,但由于众所周知的某些原因,Android 中的 Java 版本被限定在了 1.6 以下,所以也就没办法使用那么好的 Lambda 表达式了。
但 Lambda 表达式这么好,你不让我用我就不用了么?我偏要用!
好的,有以下两种方式都可以为我们开启 Lambda 表达式,我们只需要任选其一就可以了。
RetroLambda 的 Gradle 插件让我们可以在 Android 中使用 Lambda 表达式,那么我们看看如何使用它吧。
我们需要在项目目录下的build.gradle
中加入它的classpath
1 | buildscript { |
在dependencies
中加入classpath 'me.tatarka:gradle-retrolambda:3.2.5'
编辑build.gradle
启用插件,并把 Java 语法调整到 1.8
1 | apply plugin: 'me.tatarka.retrolambda' |
android
中加入以下代码段启用 1.8 的语法1 | compileOptions { |
Enjoy it !~
jack 是 Java Android Compile Kit 的缩写,它是 Google 为 Android 推出的一个编译工具包,它的原理在这里就不详述了。它有一个特点就是可以使用 Lambda 表达式,而且配置十分简单。
使用 jack 我们必须要把buildTools
升级到24以上,我已经升级到了24 RC
。
编辑 app 模块中的build.gradle
,在defaultConfig
中加一行 useJack true
,然后在android
中添加如下一段代码
1 | compileOptions { |
然后就好了,是不是非常简单呢~
在 Java 1.8 中的 Lambda 表达式实际上只是一个语法糖,它可以帮助我们简化代码,并且表述地更佳清晰。但 Java 目前来说并不是一门有函数式特性的编程语言,而且短期内不会加入函数式特性。如果你想使用一门拥有函数式特性的语言来写 Android Application 的话,可以考虑一下 Kotlin。
准备一个抽象类,将部分逻辑以具体方法以及具体构造子的形式实现,然后声明一些抽象方法来迫使子类实现剩余逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。模版方法模式是基于继承的代码复用的基本技术。
模板方法模式需要开发抽象类和具体子类的设计师之间的协作。一个设计师负责给出一个算法的轮廓和骨架,另一些设计师则负责给出这个算法的各个逻辑步骤。代表这些具体逻辑步骤的方法称做基本方法(primitive method);而将这些基本方法汇总起来的方法叫做模板方法(template method),这个设计模式的名字就是从此而来。
模板方法所代表的行为称为顶级行为,其逻辑称为顶级逻辑。模板方法模式的静态结构图如下所示:
这里涉及到两个角色:
抽象模板(Abstract Template)角色:
具体模板(Concrete Template)角色:
抽象模板角色类,abstractMethod()
、doHookMethod()
等基本方法是顶级逻辑的组成步骤,这个顶级逻辑由templateMethod()
方法代表。
1 | public abstract class AbstractTemplate { |
具体模板角色类,实现了父类所声明的基本方法,abstractMethod()
方法所代表的就是强制子类实现的剩余逻辑,而doHookMethod()
方法是可选择实现的逻辑,不是必须实现的。
1 | public class ConcreteTemplate extends AbstractTemplate { |
模板方法模式的关键:子类可以置换掉父类的可变部分,但是子类却不可以改变模板方法所代表的顶级逻辑。
每当定义一个新的子类时,不要按照控制流程的思路去想,而应当按照责任的思路去想。换言之,应当考虑哪些操作是必须置换掉的,哪些操作是可以置换掉的,以及哪些操作是不可以置换掉的。使用模板模式可以使这些责任变得清晰。
使用过Servlet的人都清楚,除了要在web.xml做相应的配置外,还需继承一个叫HttpServlet的抽象类。HttpService类提供了一个service()
方法,这个方法调用七个do方法中的一个或几个,完成对客户端调用的响应。这些do方法需要由HttpServlet的具体子类提供,因此这是典型的模板方法模式。下面是service()
方法的源代码:
1 | protected void service(HttpServletRequest req, HttpServletResponse resp) |
当然,这个service()
方法也可以被子类置换掉。
下面给出一个简单的 Servlet 例子:
TestServlet 类是 HttpServlet 类的子类,并且置换掉了父类的两个方法:doGet()
和doPost()
:
1 | public class TestServlet extends HttpServlet { |
从上面的例子可以看出这是一个典型的模板方法模式。
HttpServlet 担任抽象模板角色
service()
方法担任。doPost()
、doGet()
等方法担任。TestServlet 担任具体模板角色
doGet()
和doPost()
。单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
单例模式有以下特点:
饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。
1 | public class Singleton { |
懒汉式则是在调用获取实例对象的方法时检查,若没有对象则创建对象,如果单例对象已经存在则不创建对象直接返回已存在的对象。
1 | public class Singleton { |
这种懒汉式实现是非线程安全的,并发环境下很可能出现多个Singleton实例。若要保证线程安全,我们可以使用如下几种方式
getInstance()
方法1 | public static synchronized Singleton getInstance() { |
1 | public static Singleton getInstance() { |
1 | public class Singleton { |
其中第三种实现方式最好,避免了加锁的效率问题。但实际开发中饿汉式使用较多。