kotlin学习之泛型

取自: https://www.kotlincn.net/docs/reference/generics.html

泛型

与java类似,kotlin中的类也可以有类型参数:

class Box<T>(t: T){
    var value = t
}

一般来说,要创建这样类的实例,我们需要提供类型参数:

val box: Box<Int> = Box<Int>(1)

但是如果类型参数可以推断出来,例如从构造函数的参数或者其他途径,允许省略类型参数:

val box = Box(1)  //1具有类型Int,所以编译器知道我们说的是Box<Int>。
型变

java类型系统中最棘手的部分之一就是通配符类型。而kotlin中没有。相反,它有两个其他的东西:声明出型变 (declaration-site variance) 与类型投影(type projections)。

首先,让我们思考为什么java需要那些神秘的通配符。在《Effective Java》第三版解释了该问题——第31条:利用有限制通配符来提升API的灵活性。 首先,Java中的泛型是不型变的,这意味着 List<String> 并不是 List<Object>的子类型。为什么会这样?如果 List是 不型变的,它就没比Java的数组好到哪去,因为如下代码会通过编译然后导致运行时异常:

// Java
List<String> strs = new ArrayList<String>();
List<Object> objs = strs; // !!!即将来临的问题的原因就在这里。Java 禁止这样!
objs.add(1); // 这里我们把一个整数放入一个字符串列表
String s = strs.get(0); // !!! ClassCastException:无法将整数转换为字符串

因此,Java禁止这样的事情以保证运行时的安全。但这样会有一些影响。例如,考虑 Collection接口中的 addAll()方法。该方法的签名应该是什么?直觉上,我们会这样:

// Java
interface Collection<E> …… {
  void addAll(Collection<E> items);
}

但随后,我们将无法做到以下简单的事情(这是完全完全):

// Java
void copyAll(Collection<Object> to, Collection<String> from) {
  to.addAll(from);
  // !!!对于这种简单声明的 addAll 将不能编译:
  // Collection<String> 不是 Collection<Object> 的子类型
}

在Java中,我们艰难的学到了这个教训,参见《Effective Java》第三版,第28条:列表优先于数组

这就是为什么 addAll()的实际签名时以下这样:

// Java
interface Collection<E> …… {
  void addAll(Collection<? extends E> items);
}

通配符类型参数 ? Extends E表示此方法接受E或者E的一些子类型对象的集合,而不只是E自身。这意味着我们可以安全地从其中(该集合中的元素时E的子类的实例)读取 E,但是不能写入,因为我们不知道什么对象符合那个未知的E的子类型。反过来,该限制可以让Collection<String>表示为 Collection< ? Extends Object>的子类型。简而言之,带extends限定(上界)的通配符类型是的类型时协变的(covariant)

理解为什么这个技巧能够工作的关键相当简单:如果只能从集合中获取项目,那么使用String的集合,并且从其中读取Object也没问题。反过来,如果只能向集合中放入项目,就可以用Object集合并向其中放入String:在Java中有List<? super String>List<Object>的一个超类。性

后者成为逆变性(contravariance),并且对于List<? supper String>你只能调用接受String作为参数的方法(例如,你可以调用add(String) 或者set(int,String)),当然如果调用函数返回List<T>中的T,你得到的并非一个String 而是一个 Object

Joshua Bloch称那些你只能从中 读取 的对象为 生产者 ,并称那么你只能 写入 的对象为 消费者 。他建议:“为了灵活性最大化,在表示生产者或消费者的输入参数上使用通配符类型 ”,并提出了以下助记符:
PECS 代表生产者-Extens,消费者-Super (Producer-Extends, Consumer-Super)。

注意:如果你使用一个生产者对象,如 List<? Extends Foo> ,在该对象上不允许调用 add()set() 。但着并不意味着该对象是 不可变的 : 例如,没有什么阻止你调用 clear() 从列表中删除所有项目,因为 clear() 根本无需任何参数。通配符(或者其他类型的型变)保证的唯一的事情是 类型安全 。不可变性完全是另一回事。

声明处型变

假设有一个泛型接口 Source<T>,该接口中不存在任何以T作为参数的方法,知识方法返回 T类型值:

//Java
interface Source<T> {
	T next();
}

那么,在 Source<Object> 类型的变量中存储 Source<String> 实例的引用是极为安全的——没有消费者-方法可以调用。但是Java并不知道这一点,并且仍然禁止这样操作:

//Java
void demo(Source<String> strs>{
	Source<Object> objects  = strs;   //!!!  在Java中不允许
	//...
}

为了修正这一点,我们必须声明对象的类型为 Source<? extends Object> ,这是毫无意义的,因为我们可以像以前一样在该对象上调用所有相同的方法,所以更复杂的类型并没有带来价值。但编译器并不知道。

在Kotlin中,有一种方法向编译器解释这种情况。这称为声明处型变: 我们可以标注Source类型参数 T来确保它仅从 Source<T>成员中返回(生产),并从不被消费。 为此,我们提供out修饰符:

interface Source<out T> {
	fun nextT():T
}

fun demo(strs: Source<Sting>) {
	val objects: Source<Any> = strs //这个没问题, 因为 T 是一个 out-参数
}

一般原则是:当一个类 C的类型参数 T被声明为out时,它就只能出现在CT生产者,而不是 T消费者

out修饰符成为型变注解,并且由于它在类型参数声明处提供,所以我们称之为声明处型变。这与Java的使用处型变相反,其类型用途通配符是的类型协变。

另外除了out,Kotlin又补充了一个型变注释: in。它使得一个类型参数逆变:只可以被消费而不可以被生产。逆变类型的一个很好的例子是Comparable

interface Comparable<in T> {
	operator fun compareTo(other: T): Int
}

fun demo(x: Comparable<Number>) {	
	x.compareTo(1.0) 	//1.0拥有类型 Double,它是Number的子类型
	//因此,我们可以将x 赋给类型为 Comparable <Double>的变量
	val y: Comparanle<Double> = x 	//ok
}

我们相信 inout 两词是自解释的(因为它们已经在C# 中成功使用很长时间了),因此上面提到的助记符不是真正需要的,并且可以将其改写为更高的目标:

**存在性(The Existential)转换: 消费者 in,生产者out!

类型投影
使用处型变:类型投影

将类型参数T 声明为 out 非常方便,并且能避免使用处子类型化的麻烦,但是有些类实际上不能 限制为只返回T! 一个很好的例子是Array:

class Array<T>(val size: Int) {	
	fun get(index: Int): T {...}
	fun set(index: Int, value: T) {...}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SQLAlchemy 是一个 SQL 工具包和对象关系映射(ORM)库,用于 Python 编程语言。它提供了一个高级的 SQL 工具和对象关系映射工具,允许开发者以 Python 类和对象的形式操作数据库,而无需编写大量的 SQL 语句。SQLAlchemy 建立在 DBAPI 之上,支持多种数据库后端,如 SQLite, MySQL, PostgreSQL 等。 SQLAlchemy 的核心功能: 对象关系映射(ORM): SQLAlchemy 允许开发者使用 Python 类来表示数据库表,使用类的实例表示表中的行。 开发者可以定义类之间的关系(如一对多、多对多),SQLAlchemy 会自动处理这些关系在数据库中的映射。 通过 ORM,开发者可以像操作 Python 对象一样操作数据库,这大大简化了数据库操作的复杂性。 表达式语言: SQLAlchemy 提供了一个丰富的 SQL 表达式语言,允许开发者以 Python 表达式的方式编写复杂的 SQL 查询。 表达式语言提供了对 SQL 语句的灵活控制,同时保持了代码的可读性和可维护性。 数据库引擎和连接池: SQLAlchemy 支持多种数据库后端,并且为每种后端提供了对应的数据库引擎。 它还提供了连接池管理功能,以优化数据库连接的创建、使用和释放。 会话管理: SQLAlchemy 使用会话(Session)来管理对象的持久化状态。 会话提供了一个工作单元(unit of work)和身份映射(identity map)的概念,使得对象的状态管理和查询更加高效。 事件系统: SQLAlchemy 提供了一个事件系统,允许开发者在 ORM 的各个生命周期阶段插入自定义的钩子函数。 这使得开发者可以在对象加载、修改、删除等操作时执行额外的逻辑。
SQLAlchemy 是一个 SQL 工具包和对象关系映射(ORM)库,用于 Python 编程语言。它提供了一个高级的 SQL 工具和对象关系映射工具,允许开发者以 Python 类和对象的形式操作数据库,而无需编写大量的 SQL 语句。SQLAlchemy 建立在 DBAPI 之上,支持多种数据库后端,如 SQLite, MySQL, PostgreSQL 等。 SQLAlchemy 的核心功能: 对象关系映射(ORM): SQLAlchemy 允许开发者使用 Python 类来表示数据库表,使用类的实例表示表中的行。 开发者可以定义类之间的关系(如一对多、多对多),SQLAlchemy 会自动处理这些关系在数据库中的映射。 通过 ORM,开发者可以像操作 Python 对象一样操作数据库,这大大简化了数据库操作的复杂性。 表达式语言: SQLAlchemy 提供了一个丰富的 SQL 表达式语言,允许开发者以 Python 表达式的方式编写复杂的 SQL 查询。 表达式语言提供了对 SQL 语句的灵活控制,同时保持了代码的可读性和可维护性。 数据库引擎和连接池: SQLAlchemy 支持多种数据库后端,并且为每种后端提供了对应的数据库引擎。 它还提供了连接池管理功能,以优化数据库连接的创建、使用和释放。 会话管理: SQLAlchemy 使用会话(Session)来管理对象的持久化状态。 会话提供了一个工作单元(unit of work)和身份映射(identity map)的概念,使得对象的状态管理和查询更加高效。 事件系统: SQLAlchemy 提供了一个事件系统,允许开发者在 ORM 的各个生命周期阶段插入自定义的钩子函数。 这使得开发者可以在对象加载、修改、删除等操作时执行额外的逻辑。
GeoPandas是一个开源的Python库,旨在简化地理空间数据的处理和分析。它结合了Pandas和Shapely的能力,为Python用户提供了一个强大而灵活的工具来处理地理空间数据。以下是关于GeoPandas的详细介绍: 一、GeoPandas的基本概念 1. 定义 GeoPandas是建立在Pandas和Shapely之上的一个Python库,用于处理和分析地理空间数据。 它扩展了Pandas的DataFrame和Series数据结构,允许在其中存储和操作地理空间几何图形。 2. 核心数据结构 GeoDataFrame:GeoPandas的核心数据结构,是Pandas DataFrame的扩展。它包含一个或多个列,其中至少一列是几何列(geometry column),用于存储地理空间几何图形(如点、线、多边形等)。 GeoSeries:GeoPandas中的另一个重要数据结构,类似于Pandas的Series,但用于存储几何图形序列。 二、GeoPandas的功能特性 1. 读取和写入多种地理空间数据格式 GeoPandas支持读取和写入多种常见的地理空间数据格式,包括Shapefile、GeoJSON、PostGIS、KML等。这使得用户可以轻松地从各种数据源中加载地理空间数据,并将处理后的数据保存为所需的格式。 2. 地理空间几何图形的创建、编辑和分析 GeoPandas允许用户创建、编辑和分析地理空间几何图形,包括点、线、多边形等。它提供了丰富的空间操作函数,如缓冲区分析、交集、并集、差集等,使得用户可以方便地进行地理空间数据分析。 3. 数据可视化 GeoPandas内置了数据可视化功能,可以绘制地理空间数据的地图。用户可以使用matplotlib等库来进一步定制地图的样式和布局。 4. 空间连接和空间索引 GeoPandas支持空间连接操作,可以将两个GeoDataFrame按照空间关系(如相交、包含等)进行连接。此外,它还支持空间索引,可以提高地理空间数据查询的效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值