duangsuse::Echo
717 subscribers
4.25K photos
130 videos
583 files
6.47K links
import this:
美而不丑、明而不暗、短而不凡、长而不乱,扁平不宽,读而后码,行之天下,勿托地上天国。
异常勿吞,难过勿过,叹一真理。效率是很重要,盲目最是低效。
简明是可靠的先验,不是可靠的祭品。
知其变,守其恒,为天下式;穷其变,知不穷,得地上势。知变守恒却穷变知新,我认真理,我不认真。

技术相干订阅~
另外有 throws 闲杂频道 @dsuset
转载频道 @dsusep
极小可能会有批评zf的消息 如有不适可退出
suse小站(面向运气编程): https://WOJS.org/#/
Download Telegram
Maven 🤔 我觉得我需要 subproject
#Java #Kotlin #JVM #CS #PL #dev 🤔

duangsuse 发现自己对 Java 在集合类里泛型问题的又有了新一层的理解。

为什么容器类需要用泛型?

因为肯定是希望 List<Animal> 能够去 addAll(List<Cat>) 的呢。
Java 里默认的泛型形变,是 Invarant(不变) 的
这种情况下,虽然我们知道 Cat 是 Animal,但是 List<Cat> 却和 List<Animal> 不存在子类型 subtyping 关系!
如果我们要编写一个 addAll(??? other) 函数,保证类型安全(List<Cat> 显然不应该保存 List<Animal>!),并且让 Java 的类型检查器可以做检查的话,就必须指定泛型型变!

在这里,根据 Java 的 PECS(Producer-Extends-Consumer-Super)原则,我们知道 addAll 的类型,显然应该是 void addAll(List<? extends T> input);
但是为什么呢? 🤔

这里你只会了解到和程序设计语言类型有关的信息,Kotlin 的类型投影什么的不会讲。


泛型的『型变』是什么?
泛型的型变就是告诉 Java 类型检查器,一种泛型类的子类型关系,和它的参数化类型有什么样的联系。
这样,我们就可以实现类型安全的容器类泛化类型操作。

+ 协变(covariant):Cat 是 Animal,List<Cat> 也是 List<Animal>
+ 逆变(contravariant):Cat 是 Animal,List<Animal> (这时可能被用作输出) 是 List<Cat>
+ 不变:无论 Cat 是不是 Animal,List<Cat> 和 List<Animal> 之间没有太大的关系

在这里,子类型的『级别』,父类型为小子类型为大,以 lessThan 序排行,可能类似这样:

1. Object(kotlin.Any)
2. Animal
3. Cat

Kotlin 里面,泛型的 in T 指定了可取类型的下界 — 可选类型 ? 必须是 super TT 的超类)才可以,而 Animal 的确 superCat,所以可取类型必须 (lessThan Cat)
它实现了泛型的逆变 —

有类型 P 是类型 C 的父类型,P lessThan C
类型 F<? super C>F<P> 的父类型 (_<P> lessThan _<? lessThan C>)

Kotlin 里面,泛型的 out T 指定了可取类型集合的上界 — 可取类型 ? 必须是 extends TT 的子类型)才可以,而 Cat 的确 extends fromAnimal,所以可取类型必须 (greaterThan Animal)
它实现了泛型的协变 —

有类型 P 是类型 C 的父类型,P lessThan C
类型 F<? extends C>F<P> 的子类型 (_<P> greaterThan _<? greaterThan C>)

其实我们想说,为了类型安全,List<Animal> 的容器里,可以放的东西类型应该是:

? extends Animal
也可以说是 ? greaterThan Animal,反正比 Animal 能就可以了(跑)

我们希望所有对象都的确实现了 Animal(可以装 Animal 和它的子类扩充)
所以,我们需要一个 producer — List<? extends Animal> 来喂数据,进行 addAll 操作(利用一个 Iterable<T>,这里 T 就肯定是某种 Animal, 或者说我们利用 T get(); 方法肯定会拿到 Animal

反过来说,popAll 呢?

PECS,我们知道现在该用 in T🤔

fun popAll(output: Collection<in T>): Unit

或者说,

void popAll(Collection<? super T> output);

我们不希望任何容器,包括 output 被放上不好的东西(某种不是 Animal 的东西!)
所以,当『输出到集合(使用 void add(T item); 方法)』的时候,不如反转一下子类型关系!(相对于 addAll
这样,output(装着某种 T) 就只会被 add 上一些 extends T 的东西(这个类型自己),保证了类型安全

这就使用了泛型的逆变
非常的难受,但这居然还比 Gradle、CMake 要简单一点?(可能不是
duangsuse::Echo
非常的难受,但这居然还比 Gradle、CMake 要简单一点?(可能不是
看了一下 IDEA 的 #Java 语言版本提示,我默写一下:

1.3: Plain Old Java (2000)
1.4: assert keyword (2002)
5: Autoboxing, enum, generics (2004)
6: @Override in interfaces (2006)
7: ARM, Diamond, multi-catch (2011)
8: Lambda and Type annotations (2013)
9: Modules, private fields in interfaces (2017)
10: Local variable inference (2018)
11: Local variable syntax for lambda parameters (2018)

没有成功,但是也快了,只是 Java 7 的默写错了,别的有漏的。
啊,提起 Java,这门『垃圾』语言(和 CSharp、Kotlin、Rust 等相比就是垃圾了,差一点没 Julia、R、Fortran 之类的厉害?'跑,,,,),也未必是被大部分人完全理解的呢。

那么我们今天来完全理解一下,自 1995 年从 Oak 更名,1997 年的 Java 1.1 开始,之间提到 2004 年语法糖飞跃的 Java 5,再到最近 2018 年的 Java 11 结束,我们会看到所有的 Java 语法。(其实语法上的已经足够浅层次了,但是即使这样,很多人都能注意到自己没见过的 Java 语法)

当然,定义什么的冗长(好歹 James 老头子也是博士学位啊!)所以得感谢 ANTLR

🤔 #Learn #PL #OOP
好吧,貌似没时间,跑路先了 😶(鸽
IDEA 自动生成代码,爽到。 😆
完成一些很无聊的结构体超有成就感(跑
This media is not supported in your browser
VIEW IN TELEGRAM
This media is not supported in your browser
VIEW IN TELEGRAM
Forwarded from 羽毛的小白板
我 tm 想骂人了。昨天好心劝一个人不要用正则匹配 HTML,而他就是不相信,今天非得给自己加大难度硬来

#野群萌新
🤪
不是一个人(这个真的是极品!)。不是一个人,实际上很多工程师都这么做... 讲道理,我觉得他们应该去学学 parser combinator style parsers...

再说,就是 Java 也提供了 XML 解析库啊(Simple Api for Xml)... HTML 的也不难找,至于写爬虫什么的也可以说是必须品,除了一些情况有些人会利用 regex 之外,正规的做法是 dom parser + XPath / CSS Selector. 基本都是这样的,Ruby 的话,创建 HTML 可以用 Builder,解析可以用 Nokogiri.
This media is not supported in your browser
VIEW IN TELEGRAM
我思量着他们写出那么多看起来高大上的代码为什么会不会写解析器.... 很困难么?

大概是应用层的东西做多了,我看到 ++nInteger.MAX_VALUE 这种代码和一些 Provider 模式都开始觉得高大上...
看看复杂的控制结构,结果发现其实是不良实践... 我觉得我就是想分析得太深了,不需要测试数据就根据程序乱猜?
https://github.com/by-syk/SP

🤔 现在想起来,其实如果能进行快速的工程的话,就可以写出很多这种封装库。
可惜只有下周了,这周还有 BinaryStreamIO 和 SimpleCat

SharedPreferences sharedPreferences = context.getSharedPreferences(context
.getPackageName(), Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("user", "By_syk");
editor.apply();

这是 Android 的 SharePreference 裸 API

SP sp = new SP(context);

// Create and Update
sp.save("user", "By_syk");
sp.put("num", 5)
.put("valid", true)
.save();

// Read
String user = sp.getString("user");

// Delete
sp.delete("user");
sp.remove("num")
.remove("valid")
.save();


如果使用 SharedPreferenceProxy,你可以在 Android 上用面向接口编程,节省自己的时间:

@AndroidPreference(private = true, name = USE_PACKAGE_NAME, naming = NamingStrategy.SNACK_CASED)
interface ApplicationPreference {
String getUser();
void setUser(String value);
PreferenceTransactionBuilder user(String newValue);
}

然后

PreferenceProxy cfg = new PreferenceProxy(ApplicationPreference.class);

接着就可以:

// Create (map like)
cfg.put("name", "value object");
cfg
.make("name1", "value1")
.commit();
cfg
.update("name1", "value2")
.commit();
// Update
cfg.setUser("duangsuse");
// Read
cfg.getUser(); //=> "duangsuse"
cfg.get("user"); //=> "duangsuse"
// Delete
cfg.delete("user");
cfg
.purge("user")
.commit();
duangsuse::Echo
https://github.com/by-syk/SP 🤔 现在想起来,其实如果能进行快速的工程的话,就可以写出很多这种封装库。 可惜只有下周了,这周还有 BinaryStreamIO 和 SimpleCat SharedPreferences sharedPreferences = context.getSharedPreferences(context .getPackageName(), Context.MODE_PRIVATE); SharedPreferences.Editor editor…
总的来说,PreferencePrxoy 要有以下方法(类似,因为要有特殊类型的重载):

== Read

PreferencePrxoy#get(String):Any
PreferencePrxoy#contains(String):Boolean

== Create

PreferenceTransactionBuilder#make(String, Any):PreferenceTransactionBuilder
PreferenceTransactionBuilder#ensure(String, Any):PreferenceTransactionBuilder

== Update

PreferenceProxy#set(String, Any):Unit
PreferenceTransactionBuilder#update(String, Any):PreferenceTransactionBuilder
PreferenceProxy#put(String, Any):PreferenceProxy

== Delete

PreferenceTransactionBuilder#purge(String):Unit
PreferenceProxy#delete(String):Any
PreferenceProxy#remove(String):PreferenceProxy
PreferenceProxy#destroy():Unit


最后,它要识别 interface 里的 Getter、Setter、Transaction interface,并且检查附属的 annotation(对于整个 Preference,要指定名称和可见性以及属性命名方式,对于字段可能要指定自己的属性名、存储方式)

不打算提供其他的函数式接口,存储方式可能是 Java serializable ObjectOutputStream + Base64 或者 JSON

为了使得库接口更加可移植,使用的 Preference 必须只是一个接口(能完成提供 API 的),实际实现使用 Delegate,这样可以兼容更多的底层存储系统
可惜这周只能写这点东西 🤔 写完也好,算是 good ending( 起码学了 Maven 怎么用、Java 的 Input Stream 们、InputStreamReader、DataInputStream 的抽象,以后写二进制流式读写和 parser combinator 不用愁