【Java原理系列】 scala JavaConverters原理用法示例源码详解

【Java 工具类 Hutool】类型转换工具类-Convert 在Java开发中我们要面对各种各样的类型转换问题,尤其是从命令行获取的用户参数、从HttpRequest获取的Parameter等等,这些参数类型多种多样,我们怎么去转换他们呢?常用的办法是先整成String,然后调用XXX.parseXXX方法,还要承受转换失败的风险,不得不加一层try catch,这个小小的过程混迹在业务代码中会显得非常难看和臃肿。 阅读详情

scala JavaConverters原理用法示例源码详解

原理

JavaConverters对象提供了一组装饰器类和方法,用于在Scala集合和Java集合之间进行转换。它的原理是利用隐式转换(Implicit Conversion)和隐式类(Implicit Class)来实现。

在JavaConverters对象中,定义了多个隐式转换方法,这些方法会被自动应用于需要进行集合转换的地方。这些隐式转换方法接受一个源类型的集合对象,并返回一个装饰器对象,该装饰器对象包含了相应的转换方法,用于将源类型的集合转换为目标类型的集合。

例如,asJavaIteratorConverter方法是一个隐式转换方法,它接受一个scala.collection.Iterator类型的对象,并返回一个AsJava[java.util.Iterator]类型的装饰器对象。通过这个装饰器对象,可以调用asJava方法将Scala的迭代器转换为Java的迭代器。

同时,JavaConverters对象还使用了隐式类(Implicit Class)。隐式类是在Scala 2.10版本引入的特性,它允许在类的定义周围包裹隐式转换方法。通过定义隐式类,可以为指定的类型添加额外的方法,而无需显式地调用转换方法。

在JavaConverters对象中,通过定义隐式类RichIterable、RichIterator、RichBuffer、RichSet、RichMap等,为Scala集合类型添加了一些额外的方法。这些方法可以方便地进行Java和Scala集合之间的转换。

当需要将Scala集合转换为Java集合时,可以使用asJava方法调用适当的装饰器对象,实现类型的转换。类似地,当需要将Java集合转换为Scala集合时,可以使用asScala方法调用适当的装饰器对象。

通过隐式转换和隐式类的机制,JavaConverters对象提供了一种简洁而方便的方式,在Scala和Java集合之间进行双向的转换操作。

示例

object JavaConvertTest extends App{
  import scala.collection.JavaConverters._

  // 将Java List转换为Scala List
  val javaList: java.util.List[Int] = java.util.Arrays.asList(1, 2, 3, 4, 5)
  val scalaList: List[Int] = javaList.asScala.toList
  println(s"Scala List: $scalaList")  // 输出:Scala List: List(1, 2, 3, 4, 5)

  // 将Java Set转换为Scala Set
  val javaSet: java.util.Set[String] = new java.util.HashSet[String]()
  javaSet.add("John")
  javaSet.add("Jane")
  val scalaSet: Set[String] = javaSet.asScala.toSet
  println(s"Scala Set: $scalaSet")  // 输出:Scala Set: Set(John, Jane)

  // 将Java Map转换为Scala Map
  val javaMap: java.util.Map[String, Int] = new java.util.HashMap[String, Int]()
  javaMap.put("Alice", 25)
  javaMap.put("Bob", 30)
  val scalaMap: Map[String, Int] = javaMap.asScala.toMap
  println(s"Scala Map: $scalaMap")  // 输出:Scala Map: Map(Alice -> 25, Bob -> 30)

  // 将Scala List转换为Java List
  val scalaList2: List[String] = List("John", "Jane")
  val javaList2: java.util.List[String] = scalaList2.asJava
  println(s"Java List: $javaList2")  // 输出:Java List: [John, Jane]

  // 将Scala Set转换为Java Set
  val scalaSet2: Set[Double] = Set(1.5, 2.5, 3.5)
  val javaSet2: java.util.Set[Double] = scalaSet2.asJava
  println(s"Java Set: $javaSet2")  // 输出:Java Set: [1.5, 2.5, 3.5]

  // 将Scala Map转换为Java Map
  val scalaMap2: Map[String, Boolean] = Map("Alice" -> true, "Bob" -> false)
  val javaMap2: java.util.Map[String, Boolean] = scalaMap2.asJava
  println(s"Java Map: $javaMap2")  // 输出:Java Map: {Alice=true, Bob=false}
}

源码分析

以上源码定义了一个JavaConverters对象,其中包含了一组装饰器类和方法,用于在Scala集合和Java集合之间进行转换。

这些转换通过asJava和asScala方法实现:

  • scala.collection.Iterable <=> java.lang.Iterable
  • scala.collection.Iterator <=> java.util.Iterator
  • scala.collection.mutable.Buffer <=> java.util.List
  • scala.collection.mutable.Set <=> java.util.Set
  • scala.collection.mutable.Map <=> java.util.Map
  • scala.collection.mutable.concurrent.Map <=> java.util.concurrent.ConcurrentMap

在所有情况下,从源类型到目标类型的转换再返回原始源对象。例如:

import scala.collection.JavaConverters._

val sl = new scala.collection.mutable.ListBuffer[Int]
val jl: java.util.List[Int] = sl.asJava
val sl2: scala.collection.mutable.Buffer[Int] = jl.asScala
assert(sl eq sl2)

还支持以下转换,但是从Scala到Java的方向是通过更具体名称的方法实现的:asJavaCollection、asJavaEnumeration、asJavaDictionary:

  • scala.collection.Iterable <=> java.util.Collection
  • scala.collection.Iterator <=> java.util.Enumeration
  • scala.collection.mutable.Map <=> java.util.Dictionary

此外,还通过asJava提供了以下单向转换:

  • scala.collection.Seq => java.util.List
  • scala.collection.mutable.Seq => java.util.List
  • scala.collection.Set => java.util.Set
  • scala.collection.Map => java.util.Map

该对象继承了DecorateAsJava和DecorateAsScala trait,提供了对这些装饰器类和方法的访问。通过使用JavaConverters对象,我们可以方便地在Scala集合和Java集合之间进行转换。

中文源码

JavaConverters

package scala
package collection

import convert._


/** 一组装饰器,允许使用 `asScala` 和 `asJava` 方法在 Scala 和 Java 集合之间进行转换。
 *
 * 以下转换支持通过 `asJava`、`asScala` 进行:
 *
 * - `scala.collection.Iterable` <=> `java.lang.Iterable`
 * - `scala.collection.Iterator` <=> `java.util.Iterator`
 * - `scala.collection.mutable.Buffer` <=> `java.util.List`
 * - `scala.collection.mutable.Set` <=> `java.util.Set`
 * - `scala.collection.mutable.Map` <=> `java.util.Map`
 * - `scala.collection.mutable.concurrent.Map` <=> `java.util.concurrent.ConcurrentMap`
 *
 * 在所有情况下,从源类型到目标类型再返回原始源对象,例如:
 * {{{
 *   import scala.collection.JavaConverters._
 *
 *   val sl = new scala.collection.mutable.ListBuffer[Int]
 *   val jl : java.util.List[Int] = sl.asJava
 *   val sl2 : scala.collection.mutable.Buffer[Int] = jl.asScala
 *   assert(sl eq sl2)
 * }}}
 *
 * 还支持以下转换,但是从 Scala 到 Java 的方向是由更具体命名的方法完成的:`asJavaCollection`、`asJavaEnumeration`、`asJavaDictionary`。
 *
 * - `scala.collection.Iterable` <=> `java.util.Collection`
 * - `scala.collection.Iterator` <=> `java.util.Enumeration`
 * - `scala.collection.mutable.Map` <=> `java.util.Dictionary`
 *
 * 此外,还通过 `asJava` 提供了以下单向转换:
 *
 * - `scala.collection.Seq` => `java.util.List`
 * - `scala.collection.mutable.Seq` => `java.util.List`
 * - `scala.collection.Set` => `java.util.Set`
 * - `scala.collection.Map` => `java.util.Map`
 *
 * 作者:Martin Odersky
 * 自版本:2.8.1
 */
object JavaConverters extends DecorateAsJava with DecorateAsScala

DecorateAsJava

/**
 * 一个装饰器集合,允许使用`asScala`和`asJava`方法在Scala和Java集合之间进行转换。
 *
 * 支持以下转换通过`asJava`、`asScala`方法:
 *
 * - `scala.collection.Iterable` <=> `java.lang.Iterable`
 * - `scala.collection.Iterator` <=> `java.util.Iterator`
 * - `scala.collection.mutable.Buffer` <=> `java.util.List`
 * - `scala.collection.mutable.Set` <=> `java.util.Set`
 * - `scala.collection.mutable.Map` <=> `java.util.Map`
 * - `scala.collection.mutable.concurrent.Map` <=> `java.util.concurrent.ConcurrentMap`
 *
 * 在所有情况下,从源类型到目标类型的转换再返回原始源对象,例如:
 * {{{
 *   import scala.collection.JavaConverters._
 *
 *   val sl = new scala.collection.mutable.ListBuffer[Int]
 *   val jl : java.util.List[Int] = sl.asJava
 *   val sl2 : scala.collection.mutable.Buffer[Int] = jl.asScala
 *   assert(sl eq sl2)
 * }}}
 *
 * 还支持以下转换,但是方向 Scala 到 Java 是通过更具体命名的方法完成的:
 * `asJavaCollection`、`asJavaEnumeration`、`asJavaDictionary`。
 *
 * - `scala.collection.Iterable` <=> `java.util.Collection`
 * - `scala.collection.Iterator` <=> `java.util.Enumeration`
 * - `scala.collection.mutable.Map` <=> `java.util.Dictionary`
 *
 * 此外,还提供了以下单向转换通过`asJava`实现:
 *
 * - `scala.collection.Seq` => `java.util.List`
 * - `scala.collection.mutable.Seq` => `java.util.List`
 * - `scala.collection.Set` => `java.util.Set`
 * - `scala.collection.Map` => `java.util.Map`
 *
 * 作者:Martin Odersky
 * 自2.8.1版本以来
 */
trait DecorateAsJava {
  /**
   * 添加一个`asJava`方法,它隐式地将Scala的`Iterator`转换为Java的`Iterator`。
   * 返回的Java `Iterator`由提供的Scala `Iterator`支持,通过Java接口使用它的任何副作用都会通过Scala接口可见,反之亦然。
   *
   * 如果Scala `Iterator`先前是通过`asIterator(java.util.Iterator)`的隐式或显式调用获得的,
   * 那么`asJava`方法将返回原始的Java `Iterator`。
   *
   * @param i 要转换的`Iterator`。
   * @return 具有`asJava`方法的对象,该方法返回参数的Java `Iterator`视图。
   */
  implicit def asJavaIteratorConverter[A](i: Iterator[A]): AsJava[ju.Iterator[A]] =
    new AsJava(asJavaIterator(i))

  /**
   * 添加一个`asJavaEnumeration`方法,它隐式地将Scala的`Iterator`转换为Java的`Enumeration`。
   * 返回的Java `Enumeration`由提供的Scala `Iterator`支持,通过Java接口使用它的任何副作用都会通过Scala接口可见,反之亦然。
   *
   * 如果Scala `Iterator`先前是通过`asIterator(java.util.Enumeration)`的隐式或显式调用获得的,
   * 那么将返回原始的Java `Enumeration`。
   *
   * @param i 要转换的`Iterator`。
   * @return 具有`asJavaEnumeration`方法的对象,该方法返回参数的Java `Enumeration`视图。
   */
  implicit def asJavaEnumerationConverter[A](i: Iterator[A]): AsJavaEnumeration[A] =
    new AsJavaEnumeration(i)

  /**
   * 添加一个`asJava`方法,它隐式地将Scala的`Iterable`转换为Java的`Iterable`。
   * 返回的Java `Iterable`由提供的Scala `Iterable`支持,通过Java接口使用它的任何副作用都会通过Scala接口可见,反之亦然。
   *
   * 如果Scala `Iterable`先前是通过`asIterable(java.lang.Iterable)`的隐式或显式调用获得的,
   * 那么将返回原始的Java `Iterable`。
   *
   * @param i 要转换的`Iterable`。
   * @return 具有`asJavaCollection`方法的对象,该方法返回参数的Java `Iterable`视图。
   */
  implicit def asJavaIterableConverter[A](i: Iterable[A]): AsJava[jl.Iterable[A]] =
    new AsJava(asJavaIterable(i))

  /**
   * 添加一个`asJavaCollection`方法,它隐式地将Scala的`Iterable`转换为不可变的Java `Collection`。
   * 如果Scala `Iterable`先前是通过`asSizedIterable(java.util.Collection)`的隐式或显式调用获得的,
   * 那么将返回原始的Java `Collection`。
   *
   * @param i 要转换的`SizedIterable`。
   * @return 具有`asJava`方法的对象,该方法返回参数的Java `Collection`视图。
   */
  implicit def asJavaCollectionConverter[A](i: Iterable[A]): AsJavaCollection[A] =
    new AsJavaCollection(i)

  /**
   * 添加一个`asJava`方法,它隐式地将Scala的可变`Buffer`转换为Java的`List`。
   * 返回的Java `List`由提供的Scala `Buffer`支持,通过Java接口使用它的任何副作用都会通过Scala接口可见,反之亦然。
   *
   * 如果Scala的`Buffer`先前是通过`asBuffer(java.util.List)`的隐式或显式调用获得的,
   * 那么将返回原始的Java `List`。
   *
   * @param b 要转换的`Buffer`。
   * @return 具有`asJava`方法的对象,该方法返回参数的Java `List`视图。
   */
  implicit def bufferAsJavaListConverter[A](b: mutable.Buffer[A]): AsJava[ju.List[A]] =
    new AsJava(bufferAsJavaList(b))

  /**
   * 添加一个`asJava`方法,它隐式地将Scala的可变`Seq`转换为Java的`List`。
   * 返回的Java `List`由提供的Scala `Seq`支持,通过Java接口使用它的任何副作用都会通过Scala接口可见,反之亦然。
   *
   * 如果Scala的`Seq`先前是通过`asSeq(java.util.List)`的隐式或显式调用获得的,
   * 那么将返回原始的Java `List`。
   *
   * @param b 要转换的`Seq`。
   * @return 具有`asJava`方法的对象,该方法返回参数的Java `List`视图。
   */
  implicit def mutableSeqAsJavaListConverter[A](b: mutable.Seq[A]): AsJava[ju.List[A]] =
    new AsJava(mutableSeqAsJavaList(b))

  /**
   * 添加一个`asJava`方法,它隐式地将Scala的`Seq`转换为Java的`List`。
   * 返回的Java `List`由提供的Scala `Seq`支持,通过Java接口使用它的任何副作用都会通过Scala接口可见,反之亦然。
   *
   * 如果Scala的`Seq`先前是通过`asSeq(java.util.List)`的隐式或显式调用获得的,
   * 那么将返回原始的Java `List`。
   *
   * @param b 要转换的`Seq`。
   * @return 具有`asJava`方法的对象,该方法返回参数的Java `List`视图。
   */
  implicit def seqAsJavaListConverter[A](b: Seq[A]): AsJava[ju.List[A]] =
    new AsJava(seqAsJavaList(b))

  /**
   * 添加一个`asJava`方法,它隐式地将Scala的可变`Set`转换为Java的`Set`。
   * 返回的Java `Set`由提供的Scala `Set`支持,通过Java接口使用它的任何副作用都会通过Scala接口可见,反之亦然。
   *
   * 如果Scala的`Set`先前是通过`asSet(java.util.Set)`的隐式或显式调用获得的,
   * 那么将返回原始的Java `Set`。
   *
   * @param s 要转换的`Set`。
   * @return 具有`asJava`方法的对象,该方法返回参数的Java `Set`视图。
   */
  implicit def mutableSetAsJavaSetConverter[A](s: mutable.Set[A]): AsJava[ju.Set[A]] =
    new AsJava(mutableSetAsJavaSet(s))

  /**
   * 添加一个`asJava`方法,它隐式地将Scala的`Set`转换为Java的`Set`。
   * 返回的Java `Set`由提供的Scala `Set`支持,通过Java接口使用它的任何副作用都会通过Scala接口可见,反之亦然。
   *
   * 如果Scala的`Set`先前是通过`asSet(java.util.Set)`的隐式或显式调用获得的,
   * 那么将返回原始的Java `Set`。
   *
   * @param s 要转换的`Set`。
   * @return 具有`asJava`方法的对象,该方法返回参数的Java `Set`视图。
   */
  implicit def setAsJavaSetConverter[A](s: Set[A]): AsJava[ju.Set[A]] =
    new AsJava(setAsJavaSet(s))

  /**
   * 添加一个`asJava`方法,它隐式地将Scala的可变`Map`转换为Java的`Map`。
   * 返回的Java `Map`由提供的Scala `Map`支持,通过Java接口使用它的任何副作用都会通过Scala接口可见,反之亦然。
   *
   * 如果Scala的`Map`先前是通过`asMap(java.util.Map)`的隐式或显式调用获得的,
   * 那么将返回原始的Java `Map`。
   *
   * @param m 要转换的`Map`。
   * @return 具有`asJava`方法的对象,该方法返回参数的Java `Map`视图。
   */
  implicit def mutableMapAsJavaMapConverter[A, B](m: mutable.Map[A, B]): AsJava[ju.Map[A, B]] =
    new AsJava(mutableMapAsJavaMap(m))

  /**
   * 添加一个`asJavaDictionary`方法,它隐式地将Scala的可变`Map`转换为Java的`Dictionary`。
   * 返回的Java `Dictionary`由提供的Scala `Dictionary`支持,通过Java接口使用它的任何副作用都会通过Scala接口可见,反之亦然。
   *
   * 如果Scala的`Dictionary`先前是通过`asMap(java.util.Dictionary)`的隐式或显式调用获得的,
   * 那么将返回原始的Java `Dictionary`。
   *
   * @param m 要转换的`Map`。
   * @return 具有`asJavaDictionary`方法的对象,该方法返回参数的Java `Dictionary`视图。
   */
  implicit def asJavaDictionaryConverter[A, B](m: mutable.Map[A, B]): AsJavaDictionary[A, B] =
    new AsJavaDictionary(m)

  /**
   * 添加一个`asJava`方法,它隐式地将Scala的`Map`转换为Java的`Map`。
   * 返回的Java `Map`由提供的Scala `Map`支持,通过Java接口使用它的任何副作用都会通过Scala接口可见,反之亦然。
   *
   * 如果Scala的`Map`先前是通过`asMap(java.util.Map)`的隐式或显式调用获得的,
   * 那么将返回原始的Java `Map`。
   *
   * @param m 要转换的`Map`。
   * @return 具有`asJava`方法的对象,该方法返回参数的Java `Map`视图。
   */
  implicit def mapAsJavaMapConverter[A, B](m: Map[A, B]): AsJava[ju.Map[A, B]] =
    new AsJava(mapAsJavaMap(m))

  /**
   * 添加一个`asJava`方法,它隐式地将Scala的可变`concurrent.Map`转换为Java的`ConcurrentMap`。
   * 返回的Java `ConcurrentMap`由提供的Scala `concurrent.Map`支持,通过Java接口使用它的任何副作用都会通过Scala接口可见,反之亦然。
   *
   * 如果Scala的`concurrent.Map`先前是通过`asConcurrentMap(java.util.concurrent.ConcurrentMap)`的隐式或显式调用获得的,
   * 那么将返回原始的Java `ConcurrentMap`。
   *
   * @param m 要转换的Scala `concurrent.Map`。
   * @return 具有`asJava`方法的对象,该方法返回参数的Java `ConcurrentMap`视图。
   */
  implicit def mapAsJavaConcurrentMapConverter[A, B](m: concurrent.Map[A, B]): AsJava[juc.ConcurrentMap[A, B]] =
    new AsJava(mapAsJavaConcurrentMap(m))
}

这段代码是Scala标准库中的一个trait,提供了一些装饰器方法,用于在Scala集合和Java集合之间进行转换。

这个trait定义了一系列隐式转换方法,例如asJavaIteratorConverter、asJavaEnumerationConverter、asJavaIterableConverter等等。这些方法允许在Scala集合和对应的Java集合之间相互转换,以便在使用时能够无缝切换。

该trait还提供了一些单向转换方法,例如mutableSeqAsJavaListConverter、seqAsJavaListConverter、mutableSetAsJavaSetConverter等等,用于将特定类型的Scala集合转换为对应的Java集合。

DecorateAsScala

trait DecorateAsScala {
 /**
  * 添加一个`asScala`方法,它隐式地将Java的`Iterator`转换为Scala的`Iterator`。
  *
  * 返回的Scala `Iterator`由提供的Java `Iterator`支持,通过Scala接口使用它的任何副作用都会通过Java接口可见,反之亦然。
  *
  * 如果Java `Iterator`先前是通过`asIterator(scala.collection.Iterator)`的隐式或显式调用获得的,
  * 那么`asScala`方法将返回原始的Scala `Iterator`。
  *
  * @param i 要转换的`Iterator`。
  * @return 具有`asScala`方法的对象,该方法返回参数的Scala `Iterator`视图。
  */
 implicit def asScalaIteratorConverter[A](i : ju.Iterator[A]): AsScala[Iterator[A]] =
   new AsScala(asScalaIterator(i))

 /**
  * 添加一个`asScala`方法,它隐式地将Java的`Enumeration`转换为Scala的`Iterator`。
  *
  * 返回的Scala `Iterator`由提供的Java `Enumeration`支持,通过Scala接口使用它的任何副作用都会通过Java接口可见,反之亦然。
  *
  * 如果Java `Enumeration`先前是通过`asEnumeration(scala.collection.Iterator)`的隐式或显式调用获得的,
  * 那么将返回原始的Scala `Iterator`。
  *
  * @param i 要转换的`Enumeration`。
  * @return 具有`asScala`方法的对象,该方法返回参数的Scala `Iterator`视图。
  */
 implicit def enumerationAsScalaIteratorConverter[A](i : ju.Enumeration[A]): AsScala[Iterator[A]] =
   new AsScala(enumerationAsScalaIterator(i))

 /**
  * 添加一个`asScala`方法,它隐式地将Java的`Iterable`转换为Scala的`Iterable`。
  *
  * 返回的Scala `Iterable`由提供的Java `Iterable`支持,通过Scala接口使用它的任何副作用都会通过Java接口可见,反之亦然。
  *
  * 如果Java `Iterable`先前是通过`asIterable(scala.collection.Iterable)`的隐式或显式调用获得的,
  * 那么将返回原始的Scala `Iterable`。
  *
  * @param i 要转换的`Iterable`。
  * @return 具有`asScala`方法的对象,该方法返回参数的Scala `Iterable`视图。
  */
 implicit def iterableAsScalaIterableConverter[A](i : jl.Iterable[A]): AsScala[Iterable[A]] =
   new AsScala(iterableAsScalaIterable(i))

 /**
  * 添加一个`asScala`方法,它隐式地将Java的`Collection`转换为Scala的`Iterable`。
  *
  * 如果Java `Collection`先前是通过`asCollection(scala.collection.SizedIterable)`的隐式或显式调用获得的,
  * 那么将返回原始的Scala `SizedIterable`。
  *
  * @param i 要转换的`Collection`。
  * @return 具有`asScala`方法的对象,该方法返回参数的Scala `SizedIterable`视图。
  */
 implicit def collectionAsScalaIterableConverter[A](i : ju.Collection[A]): AsScala[Iterable[A]] =
   new AsScala(collectionAsScalaIterable(i))

 /**
  * 添加一个`asScala`方法,它隐式地将Java的`List`转换为Scala的可变`Buffer`。
  *
  * 返回的Scala `Buffer`由提供的Java `List`支持,通过Scala接口使用它的任何副作用都会通过Java接口可见,反之亦然。
  *
  * 如果Java的`List`先前是通过`asList(scala.collection.mutable.Buffer)`的隐式或显式调用获得的,
  * 那么将返回原始的Scala `Buffer`。
  *
  * @param l 要转换的`List`。
  * @return 具有`asScala`方法的对象,该方法返回参数的Scala可变`Buffer`视图。
  */
 implicit def asScalaBufferConverter[A](l : ju.List[A]): AsScala[mutable.Buffer[A]] =
   new AsScala(asScalaBuffer(l))

 /**
  * 添加一个`asScala`方法,它隐式地将Java的`Set`转换为Scala的可变`Set`。
  *
  * 返回的Scala `Set`由提供的Java `Set`支持,通过Scala接口使用它的任何副作用都会通过Java接口可见,反之亦然。
  *
  * 如果Java的`Set`先前是通过`asSet(scala.collection.mutable.Set)`的隐式或显式调用获得的,
  * 那么将返回原始的Scala `Set`。
  *
  * @param s 要转换的`Set`。
  * @return 具有`asScala`方法的对象,该方法返回参数的Scala可变`Set`视图。
  */
 implicit def asScalaSetConverter[A](s : ju.Set[A]): AsScala[mutable.Set[A]] =
   new AsScala(asScalaSet(s))

 /**
  * 添加一个`asScala`方法,它隐式地将Java的`Map`转换为Scala的可变`Map`。
  * 返回的Scala `Map`由提供的Java `Map`支持,通过Scala接口使用它的任何副作用都会通过Java接口可见,反之亦然。
  *
  * 如果Java的`Map`先前是通过`asMap(scala.collection.mutable.Map)`的隐式或显式调用获得的,
  * 那么将返回原始的Scala `Map`。
  * 
  * 如果包装的map是同步的(例如从`java.util.Collections.synchronizedMap`),则您需要在所有非原子操作周围使用`underlying.synchronized`进行包装。
  * 这包括`get`,因为`java.util.Map`的API不允许在可能存在`null`值时进行原子`get`。
  *
  * @param m 要转换的`Map`。
  * @return 具有`asScala`方法的对象,该方法返回参数的Scala可变`Map`视图。
  */
 implicit def mapAsScalaMapConverter[A, B](m : ju.Map[A, B]): AsScala[mutable.Map[A, B]] =
   new AsScala(mapAsScalaMap(m))

 /**
  * 添加一个`asScala`方法,它隐式地将Java的`ConcurrentMap`转换为Scala的可变`concurrent.Map`。
  * 返回的Scala `concurrent.Map`由提供的Java `ConcurrentMap`支持,通过Scala接口使用它的任何副作用都会通过Java接口可见,反之亦然。
  *
  * 如果Java的`ConcurrentMap`先前是通过`mapAsScalaConcurrentMap(scala.collection.mutable.ConcurrentMap)`的隐式或显式调用获得的,
  * 那么将返回原始的Scala `concurrent.Map`。
  *
  * @param m 要转换的`ConcurrentMap`。
  * @return 具有`asScala`方法的对象,该方法返回参数的Scala可变`concurrent.Map`视图。
  */
 implicit def mapAsScalaConcurrentMapConverter[A, B](m: juc.ConcurrentMap[A, B]): AsScala[concurrent.Map[A, B]] =
   new AsScala(mapAsScalaConcurrentMap(m))

 /**
  * 添加一个`asScala`方法,它隐式地将Java的`Dictionary`转换为Scala的可变`Map[String, String]`。
  * 返回的Scala `Map[String, String]`由提供的Java `Dictionary`支持,通过Scala接口使用它的任何副作用都会通过Java接口可见,反之亦然。
  *
  * @param p 要转换的`Dictionary`。
  * @return 具有`asScala`方法的对象,该方法返回参数的Scala可变`Map[String, String]`视图。
  */
 implicit def dictionaryAsScalaMapConverter[A, B](p: ju.Dictionary[A, B]): AsScala[mutable.Map[A, B]] =
   new AsScala(dictionaryAsScalaMap(p))

 /**
  * 添加一个`asScala`方法,它隐式地将Java的`Properties`转换为Scala的可变`Map[String, String]`。
  * 返回的Scala `Map[String, String]`由提供的Java `Properties`支持,通过Scala接口使用它的任何副作用都会通过Java接口可见,反之亦然。
  *
  * @param p 要转换的`Properties`。
  * @return 具有`asScala`方法的对象,该方法返回参数的Scala可变`Map[String, String]`视图。
  */
 implicit def propertiesAsScalaMapConverter(p: ju.Properties): AsScala[mutable.Map[String, String]] =
   new AsScala(propertiesAsScalaMap(p))
}

这段代码是Scala标准库中的一个trait,提供了一些装饰器方法,用于在Java集合和Scala集合之间进行转换。

这个trait定义了一系列隐式转换方法,例如asScalaIteratorConverter、enumerationAsScalaIteratorConverter、iterableAsScalaIterableConverter等等。这些方法允许在Java集合和对应的Scala集合之间相互转换,以便在使用时能够无缝切换。

该trait还提供了一些单向转换方法,例如asScalaBufferConverter、asScalaSetConverter、mapAsScalaMapConverter等等,用于将特定类型的Java集合转换为对应的Scala集合。

请注意,这段代码只是给出了一个示例,具体应用时可能需要根据实际需求进行适当修改和扩展。

Decorators

import java.{ util => ju }

private[collection] trait Decorators {
  /** 包含 `asJava` 转换方法的通用类 */
  class AsJava[A](op: => A) {
    /** 将 Scala 集合转换为对应的 Java 集合 */
    def asJava: A = op
  }

  /** 包含 `asScala` 转换方法的通用类 */
  class AsScala[A](op: => A) {
    /** 将 Java 集合转换为对应的 Scala 集合 */
    def asScala: A = op
  }

  /** 包含 `asJavaCollection` 转换方法的通用类 */
  class AsJavaCollection[A](i: Iterable[A]) {
    /** 将 Scala 的 `Iterable` 转换为 Java 的 `Collection` */
    def asJavaCollection: ju.Collection[A] = JavaConversions.asJavaCollection(i)
  }

  /** 包含 `asJavaEnumeration` 转换方法的通用类 */
  class AsJavaEnumeration[A](i: Iterator[A]) {
    /** 将 Scala 的 `Iterator` 转换为 Java 的 `Enumeration` */
    def asJavaEnumeration: ju.Enumeration[A] = JavaConversions.asJavaEnumeration(i)
  }

  /** 包含 `asJavaDictionary` 转换方法的通用类 */
  class AsJavaDictionary[A, B](m : mutable.Map[A, B]) {
    /** 将 Scala 的 `Map` 转换为 Java 的 `Dictionary` */
    def asJavaDictionary: ju.Dictionary[A, B] = JavaConversions.asJavaDictionary(m)
  }
}

private[collection] object Decorators extends Decorators

以上代码定义了一个名为 Decorators 的特质,其中包含多个类和方法,用于进行Scala集合与Java集合之间的相互转换。

  • AsJava 类:包含 asJava 转换方法,用于将 Scala 集合转换为对应的 Java 集合。
  • AsScala 类:包含 asScala 转换方法,用于将 Java 集合转换为对应的 Scala 集合。
  • AsJavaCollection 类:包含 asJavaCollection 转换方法,用于将 Scala 的 Iterable 转换为 Java 的 Collection。
  • AsJavaEnumeration 类:包含 asJavaEnumeration 转换方法,用于将 Scala 的 Iterator 转换为 Java 的 Enumeration。
  • AsJavaDictionary 类:包含 asJavaDictionary 转换方法,用于将 Scala 的 Map 转换为 Java 的 Dictionary。

最后,通过 Decorators 对象来使用这些转换方法。该对象是 Decorators 特质的实例化对象,提供了对外公开的接口以进行集合转换操作。

语法分析

以上源码使用了以下Scala特性:

  1. 隐式转换(Implicit Conversion):通过定义隐式转换方法,实现了从Java集合到Scala集合的自动转换。例如,asScalaIteratorConverter、enumerationAsScalaIteratorConverter等方法都是通过隐式转换实现的。

  2. 类型参数化(Type Parameterization):在定义装饰器类时,使用了类型参数来泛化方法的返回类型。例如,AsJava和AsScala类都使用了类型参数A,使得这些类可以适用于不同类型的集合。

  3. 特质(Trait):Decorators trait 是一个特质,用于定义一组相关的功能。它可以被其他类或对象混入(mixin),以扩展它们的功能。

  4. 对象(Object):Decorators对象是一个单例对象,用于提供对Decorators trait 中定义的装饰器类和方法的访问。

  5. 高阶函数(Higher-Order Functions):装饰器类中的方法接受一个传名参数,允许在需要时延迟求值。例如,AsJava和AsScala类的构造函数中的op: => A参数就是一个传名参数。

  6. 泛型集合(Generic Collection):装饰器类中的方法接受和返回泛型的集合类型,例如Iterable[A]、Collection[A]、Enumeration[A]、Dictionary[A, B]等。

  7. 包对象(Package Object):在代码中使用了mutable包对象,通过导入mutable.Map,实现对Scala可变Map的引用。

总体而言,以上源码利用了Scala的隐式转换、类型参数化、特质、对象、高阶函数、泛型集合和包对象等多种特性来实现Java集合和Scala集合之间的互相转换。

传名参数

传名参数(Call-by-Name)是一种在函数调用时,实际参数并不立即求值,而是在需要时才进行求值的机制。在Scala中,可以使用=>符号来定义传名参数。

下面是一个示例代码,展示了如何在Scala中使用传名参数:

def exampleFunc(op: => Int): Unit = {
  println("Before calling op")
  val result = op
  println(s"The result is $result")
}

def expensiveOperation(): Int = {
  println("Expensive operation...")
  42
}

// 调用exampleFunc,并传递expensiveOperation作为op参数
exampleFunc(expensiveOperation())

在上述示例中,exampleFunc函数接受一个传名参数op: => Int。当调用exampleFunc(expensiveOperation())时,expensiveOperation函数并没有立即执行,而是在op被实际使用时才进行求值。输出结果如下:

Before calling op
Expensive operation...
The result is 42

可以看到,在调用exampleFunc之前并没有打印"Expensive operation…",说明expensiveOperation并未立即执行。只有在val result = op这一行代码中,op才会被求值,进而执行expensiveOperation函数。

富接口

Rich Interface为Scala集合添加额外的方法:

import java.util.{ArrayList, List}

// 富接口类,扩展了Java的List接口
implicit class RichList[A](list: List[A]) {
  // 添加一个自定义方法,用于打印列表中的元素
  def printElements(): Unit = {
    for (i <- 0 until list.size()) {
      println(list.get(i))
    }
  }
}

// 创建一个Java的ArrayList
val javaList: ArrayList[String] = new ArrayList[String]()
javaList.add("Apple")
javaList.add("Banana")
javaList.add("Orange")

// 将Java的ArrayList转换为Scala的List,并通过富接口调用自定义方法
val scalaList: List[String] = javaList
scalaList.printElements()

在上述示例中,我们首先定义了一个RichList类,它扩展了Java的List接口。该类带有一个自定义方法printElements(),用于打印列表中的元素。

然后,我们创建了一个Java的ArrayList并向其中添加一些元素。接下来,我们将Java的ArrayList转换为Scala的List,并使用富接口调用自定义的printElements()方法,打印出列表中的元素。

输出结果如下:

Apple
Banana
Orange

通过使用富接口,我们可以为Scala集合添加自定义的方法,使其更加丰富和方便使用。这样,我们可以根据自己的需求来扩展和定制集合的功能。

从零到一:基于RK3399的VME控制器Linux驱动开发全流程(PCIE+FPGA篇) 本文详细介绍了基于RK3399和FPGA的VME控制器Linux驱动开发全流程,涵盖PCIE设备驱动开发、FPGA接口设计及系统集成调试。通过实战指南,开发者可掌握从硬件配置到软件实现的完整技术栈,特别适合工业控制系统开发。重点解析了RK3399与FPGA协同工作的关键技术要点。 阅读详情

相关推荐

基于Spark 2.2的实时新闻分析系统:从架构设计到生产级调优

实时数据处理是现代大数据技术的核心应用之一,其核心原理在于对连续不断的数据流进行即时计算与分析,以实现低延迟的业务洞察。在技术实现上,流处理系统通过高吞吐数据接入、窗口聚合与状态管理等机制,将动态数据转化为实时价值。Apache Spark Structured Streaming作为成熟的流处理框架,提供了基于DataFrame API的声明式编程模型,支持精确一次语义(Exactly-Once)和容错状态管理,极大地简化了实时数据管道的开发。其技术价值体现在能够支撑实时监控、实时推荐、舆情分析等多种高价

weixin_30642869的博客 422

JAVA—抽象—定义抽象类Converter及其子类WeightConverter

一个私有的String类型成员变量:name,表示换算器Converter的名称,并定义name变量的getter方法;一个带参数的构造器,用于对name赋值;一个公有的抽象方法:double convert();表示进行换算操作。定义Converter的子类WeightConverter,表示重量换算器。成员变量 value 表示原重量值,并定义其getter方法;成员变量 factor 表示换算系数,比如 公斤-克的换算系数为1000,因为1公斤=1000克;

2301_80682565的博客 1623

【Claude Code】Claude Code 使用指南

本文介绍了AI编码助手Claude的基础和进阶操作指南。基础部分涵盖启动命令、模式切换(自动编辑/Plan/Yolo模式)、资源监控等核心功能,重点讲解了斜杠命令的使用方法。进阶操作详细说明了CLAUDE.md文件的创建与管理,包括项目规范、代码风格等内容的配置,以及如何通过该文件建立持久上下文。同时提醒了使用过程中的安全风险,建议通过配置文件限制AI对关键代码的访问权限。全文提供了完整的操作指引和安全建议,帮助开发者高效安全地使用Claude进行编码辅助。

大家好,我是敖云岚,一个沉迷代码的 “技术玩家”。擅长 Java、C++ 开发,对 MySQL、Redis、RabbitMQ 等技术也颇有研究。博客里既有硬核技术干货,也有诙谐幽默的编程日常分享。愿和你一起,笑着学技术,快乐敲代码! 1万+

java converter转换器_Java中转换器设计模式深入讲解

前言在这篇文章中,我们将讨论 Java / J2EE项目中最常用的 Converter Design Pattern。由于Java8 功能不仅提供了相应类型之间的通用双向转换方式,而且还提供了转换相同类型对象集合的常用方法,从而将样板代码减少到绝对最小值。我们使用Java8 功能编写了此模式的源代码。目的转换器设计模式的目的是为相应类型之间的双向转换提供一种通用的方式,允许类型无需彼此了解的简洁...

weixin_39608300的博客 2453

细说STM32单片机SPI-Flash芯片的FatFS移植及其应用实例详解

本文以开发板上的SPI-Flash芯片W25Q16为媒介详细介绍CubeMX中FatFS各个参数的意义和设置,分析生成的代码中FatFS各个文件的作用和关联,完成针对W25Q16的硬件访问层的移植。继续使用旺宝红龙开发板STM32F407ZGT6 KITV1.0。

wenchm的博客 1640

用ST25DV芯片+KiCad快速设计NFC标签天线:参数计算/脚本生成/实测优化全流程

本文详细介绍了使用ST25DV芯片和KiCad工具设计NFC标签天线的全流程,包括参数计算、脚本生成和实测优化。通过解析芯片等效工作原理和设计思路,帮助开发者避开常见陷阱,实现高效、可量产的天线设计。文章还提供了Python计算工具和KiCad自动化脚本,大幅提升设计效率和成功率。

jj890的博客 758

【Java】Converter(数据类型转换工具类)

Java 简单数据类型转换工具类 /** * A simple object converter * 简单数据类型转换工具类 */ public abstract class Converter { /** * Gets a String from a Object in a null-safe manner. * &amp;amp;amp;lt;p&amp;amp;amp;gt; * Th...

XKIND'S 博客 1万+

Spring AI + RAG 构建电商智能客服:从 PDF 文档解析到精准问答的全链路实战

核心框架:向量数据库:- PostgreSQL + pgvector (生产推荐)- Redis Stack (轻量级方案)- ChromaDB (快速原型)文档解析:- Apache PDFBox: PDF 文本提取- Spring AI Document Readers: 结构化解析大模型:- 阿里云百炼 (DashScope)- 本地部署:Qwen/ChatGLM/*** 电商领域实体*/@Data@Builder@ToString/*** 商品类型。

一起修行,不孤单。这里有编程语言、开发工具、学习方法论和踩坑笔记。用简单的话说复杂的事,陪你从小白到进阶。周更干货,欢迎一起成长! 381

Scala与Java集合互转摘要

在上一篇文章《再看Kafka Lag》中笔者提及了Scala与Java集合互转的内容,但是并未有详述,本文对此做一个补充说明,方便在使用的时候可以随时翻阅。 对于集合而言,Scala从2.8.1开始引入scala.collection.JavaConverters用于Scala与Java集合的互转。在scala代码中如果需要集合转换,首先引入scala.collection.JavaConver...

朱小厮的博客 3016

Linux命令行入门:从零掌握服务器运维与Shell脚本基础

在信息化时代,操作系统是数字世界的基石,而终端命令行则是高效驾驭服务器的核心技能。无论是软件部署、服务监控还是故障排查,掌握基于Shell的指令操作都能大幅提升工程效率。从最基础的文件目录管理、文本处理,到权限控制、进程监控与网络调试,命令行提供了一套强大且可自动化的操作范式。尤其对于运维工程师和开发者而言,理解管道、重定向以及脚本化任务,意味着将重复劳动转化为可复用的自动化流程。本文从环境准备讲起,系统梳理常用Linux命令的实际场景与输出验证,并延伸至Shell脚本编写与定时任务,帮助初学者快速建立从操

weixin_30522983的博客 255

Comsol&Matlab 亥姆霍兹共振消声器传递损失理论解与仿真解

当外界噪声以共振频率进入共振器时,共振器内的空气质量发生周期性的振动,从而减少或消除噪声。当特定频率的声波进入共振器时,共振器的空气质量会发生周期性的振动,从而实现对该频率的噪声的减弱或消除。5. 反射和吸收:共振器的几何形状和容积还可以影响共振器内空气振动的反射和吸收。然而,对于不同频率的噪声,需要相应调整共振器的参数以匹配新的共振频率。3. 腔体内的空气振动:当特定频率的声波进入共振器时,共振器内的空气质量会发生振动。2. 声波进入共振器:外界的声波通过共振器的颈部进入共振器的封闭空腔。

声学黑洞仿真工作室 4575

【设计模式】JAVA Design Patterns——Converter(转换器模式)

转换器模式的目的是提供相应类型之间双向转换的通用方法,允许进行干净的实现,而类型之间无需相互了解。此外,Converter模式引入了双向集合映射,从而将样板代码减少到最少。在真实的应用中经常有这种情况,数据库层包含需要被转换成业务逻辑层DTO来使用的实体。对于潜在的大量类进行类似的映射,我们需要一种通用的方法来实现这一点。现在就可以做到User和UserDto之间的映射编简单。转换器模式让一个类的实例映射成另一个类的实例变得简单。

CSBIGDOG的博客 1602

上机任务一:空气质量指数 (air quality index,AQI是定量描述空气质量状况的指数。为了更好地了解和人们身体健康息息相关的天气状况,本书模拟生成500条某地区天气数据,一行样本对应一天

调整后的R平方为0.4308,考虑了模型中的自由度,它略低于多重R平方,说明模型的解释能力可能有限。总的来说,新的模型相对于之前的模型在AIC值上更优,包含了最高温度(X2)和最低温度(X3)这两个重要的预测因子,并且对AQI的解释能力较好。在表中,Df表示自由度,Sum Sq表示总平方和,Mean Sq表示均方(总平方和除以自由度),F value表示F统计量,Pr(>F)表示对应的p值。在分析这些结果时,需要关注系数的显著性(p值),系数的方向(正向或负向影响),以及系数的大小(估计值)。

Mrrunsen的博客 723

1 Java中的数据转换(PropertyEditor、Converter)

本文主要讨论Java、Spring对于数据的转换(Converter)、格式化(Formatter)、验证问题(Validate)

LawssssCat的博客 1571

多物理场耦合仿真-主题032-电磁-流体耦合

电磁-流体耦合,即磁流体动力学(MHD, Magnetohydrodynamics),是研究导电流体在电磁场中运动规律的学科,涉及电磁场与流体力学的深度耦合。本主题系统阐述磁流体动力学的基本理论、控制方程和数值方法。首先介绍MHD的基本概念和物理基础,包括导电流体的性质、电磁场基本方程;然后详细推导MHD控制方程组,包括连续性方程、动量方程、能量方程和电磁场方程;接着讨论MHD流动的特征参数,如Hartmann数、磁雷诺数、相互作用参数等;最后通过六个典型工程案例展示MHD分析的实际应用,包括电磁泵、磁流体

2401_87715305的博客 424

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

scala集合和java集合的转换-List

scala集合和java集合的转换-List

weixin_45538732的博客 1821

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

scala集合泛型数据类型转换成java集合遇到的问题

简介 今天在线上遇到一个很棘手的scala的Map集合转换为java的Map集合中类型转换异常的问题,百度上并没有找到相关解决方案。这特地记录一下这个问题以及解决的方案。在叙述这个问题之前觉得有必要在记录一下常用的scala和java转换的知识。熟悉scala开发的人对scala.collection.JavaConversions._和scala.collection.JavaConverter...

GalenGao 1756

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

scala java 转换_Java与Scala集合互转,Java中调用Scala集合,Scala中转换Java集合

摘要:Scala,Java,JavaConverters包括三种场景的转化Java脚本中,将Scala集合转化为Java集合Scala脚本中,将Scala集合转化为Java集合Scala脚本中,将Java集合转化为Scala集合JavaConverters隐式转换Scala提供了大量的方法来隐式转换所有主要的Java和Scala容器类型。其中提供了如下的双向类型转换Iterator ...

weixin_39595931的博客 1310

Scala容器与Java容器的互相转换

从scala 2.12版本开始,Scala官方废除了“scala.collection.JavaConversions”,改用“scala.collection.JavaConverters”。如果继续强行使用,则会提示如下错误: java.lang.NoSuchMethodError: scala.collection.JavaConversions$.deprecated$u002 0prop...

甘焕的博客 2255

【Java开发--对象converter转换规范实践】

本文介绍了Java开发中对象转换器(Converter)的实践与规范。首先展示了两种基础实现方式:通过手动复制属性和使用Builder模式进行对象转换。接着,推荐使用MapStruct工具进行自动映射,并详细说明了其优势,如编译时类型安全性和减少重复代码

Kaka_csdn14的博客 1181

java conversion_在Scala中JavaConverters和JavaConversions有什么区别?

JavaConversions提供了一系列隐式方法,可以在Java集合和最接近的对应Scala集合之间进行转换,反之亦然。这是通过创建包装器来实现的,它实现了Scala接口并将调用转发到底层Java集合或Java接口,将调用转发到底层Scala集合。JavaConverters使用pimp-my-library模式将asScala方法“添加”到Java集合中,并将asJava方法“添加”到Scal...

weixin_35651855的博客 400

java converter转换器_springboot 自定义转换器(converter)

springboot自定义转换器(converter)应用:实现一对一转换,如将字符串转换为pojo、枚举等***********************相关类与接口Converter@FunctionalInterfacepublic interface Converter {@NullableT convert(S source); //将类型S的对象转换为类型T的对象}Converter...

weixin_42489714的博客 2839

[Scala源码] Scala与Java间集合类型转换的性能分析

关于隐式转换方法,个人的理解就是:提供了一系列类型间的转换供编译器选择。当编译不成功时,编译器会去找合适的隐式方法将类型进行适当的转换从而使得编译通过。在代码中经常会涉及scala和java集合的相互转换,担心其会产生性能上的问题,今天就来看看源码分析一下转换过程带来的代价。: 这里不是传了个方法(有冒号),而是是名调用(call-by-name), 这个参数。,并不会对性能产生什么影响,只是做了个适配/包装(实现java接口的方法),并不存在集。由源码可以看出:对于列表类型的集合类型转换,时间复杂度为。

skdtr的博客 388

Java的 类型转换器工具类Convert

java工具类

牵羊逛街的狼 7696

scala java 隐式转换_使用JavaConversions在Java和Scala集合之间进行隐式转换

小编典典他们说尝试使用JavaConverters,因为不推荐使用JavaConversions。scala> import collection.JavaConverters._import collection.JavaConverters._scala> def mergeMaps[A, B](ms: Set[Map[A, B]])(f: (B, B) => B): Map...

weixin_30562757的博客 1002

java中集合类的转换_Scala 和 Java 的集合类型相互转换

在 Scala 和 Java 混合编程时免不了需要进行集合类型在两种语言间相互转换,更多的是在 Scala 调用 Java 的方法时把 Scala 的集合转型为 Java 的集合。典型场景是:public void process(java.util.List orderIds) {......}上面定义的 Java 方法,如果要在 Scala 中调用它,不考虑两种语言的集体类型转换的话,可以直接...

weixin_29941275的博客 708
上一篇: 【Spark床头书系列】Spark SparkSession由来方法示例源码分析
下一篇: 【Spark ML系列】 Kmeans聚类算法由来原理方法示例源码分析
BigDataMLApplication
博客等级 码龄11年 7363粉丝 565原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BigDataMLApplication

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值