对于想了解SwiftGenerics–调用哪种方法?的读者,本文将提供新的信息,我们将详细介绍swift方法调用原理,并且为您提供关于java泛型Generics、builder&Generics、c
对于想了解Swift Generics – 调用哪种方法?的读者,本文将提供新的信息,我们将详细介绍swift 方法调用原理,并且为您提供关于
- Swift Generics – 调用哪种方法?(swift 方法调用原理)
java 泛型 Generics - builder & Generics
- clojure 与 java 交互 (泛型 generics)
- Delphi 2009 泛型容器单元 (Generics.Collections)[4]: TDictionary
Swift Generics – 调用哪种方法?(swift 方法调用原理)
protocol AwesomeType: Equatable { var thingy: Int { get } } extension Array where Element: Equatable { func doThing { ... } } extension Array where Element: AwesomeType { func doThing { ... } } extension String: AwesomeType { var thingy: Int { return 42 } }
如果我有一个字符串数组 – [“Foo”,“Bar”,“Baz”] – 我在其上调用doThing(),将调用哪个实现?为什么?
我相信这是在编译时确定的;换句话说,它不是一个动态的调度.但它是如何确定的?感觉它类似于协议扩展的规则,但这是一个动态的调度情况……
解决方法
error: ambiguous use of ‘doThing()’
使用简单修改的示例:
protocol AwesomeType: Equatable { } extension Array where Element: Equatable { func doThing() { print("array one") } } extension Array where Element: AwesomeType { func doThing() { print("array two") } } extension String: AwesomeType { } let arr = [ "Foo","Bar","Baz" ] arr.doThing()
编译器抱怨
error: ambiguous use of ‘doThing()’
note: found this candidate
func doThing() { print(“array one”) }note: found this candidate
func doThing() { print(“array two”) }
编译器根本不知道调用哪一个,因为这两个方法具有相同的名称和参数.
具有相同但实际上不相关的多个方法总是会导致一些编译器问题的风险,当这两个方法最终在某个时刻重叠时.
java 泛型 Generics
what
泛型:一般类型,也就是说可以为任何类型.
泛型的本质是 “参数化类型”,也就是说: 所操作的数据类型 被指定为一个参数,传输。泛型是在 JDK1.5 中引入的特性。
why
泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型,而不是在运行时才出现错误。
使用泛型有以下好处:
-
编译时强类型检查
-
无需手动进行类型转换
-
可以实现复用,编写通用算法
how
泛型类
ArrayList<E>
public class Tool<E> {
private E e;
public Tool(E e1){
this.e = e1;
}
public E getE() {
return e;
}
public void setE(E e) {
this.e = e;
}
}
泛型方法
1) 使用泛型类定义参数类型 (常用)
如泛型类 Tool<AA>,它的泛型参数即为 AA。那么泛型方法可以这样写:
public class Tool<AA>{
public void show(AA aa){
}
}
2) 自定义的参数类型
public <AA> void show(AA aa){
}
注:静态方法不能访问类的泛型,如果需要泛型,我们只能使用方法 2 (自定义的参数类型)
泛型接口
假设有泛型接口 interf<AA>,它的实现类是 Tool。
1) 确定实现的泛型接口的参数类型:
假设 Tool 类需要 String 类型的参数,那么实现可以直接写:
class Tool implements interf<String>{
}
2) 不确定实现的泛型接口的参数类型:
我们需要泛型类来实现
class Tool<BB> implements interf<BB>{
}
当泛型类和泛型方法一起出现的时候
泛型 T 被实现类所指定的泛型类型替换,而参数 e 是由调用者决定的
public interface Test<T> {
<E> T aaa(E e, T t);
}
public class Demo implements Test<Integer> {
@Override
public <E> Integer aaa(E e, Integer t) {
return null;
}
}
当然返回类型也可更改为 E
public interface Test<T> {
<E> E aaa(E e, T t);
}
public class Demo implements Test<Integer> {
@Override
public <E> E aaa(E e, Integer t) {
return null;
}
}
泛型通配符:?
当我们不确定传入的对象类型时我们就可以使用?来代替。“?” 即泛型通配符。
泛型的限定
书写格式:
上限:<? extends E> 表示参数类型是 E 或其所有子类。
下限:<? super E> 表示参数类型是 E 或其所有超类 (即父类)。
约定俗成的定义
? 表示不确定的 java 类型。
T 表示 java 类型。
K, V 分别代表 java 键值中的 Key Value。
E 代表 Element。
泛型的擦除
一句话总结就是:在.java 文件运用泛型技术时,编译器在文件编译通过后自动擦除泛型标识。
泛型的补偿
编译器在擦除泛型后,会自动将类型转换为原定义的 "泛型",这样就不必再做向下类型转换了。 泛型的擦除和补偿 这两个机制都是编译器内部自动完成的,了解其原理即可。
builder & Generics
package com.eloancn.back.submitted.service.helper; import java.util.Date; import java.util.HashMap; import java.util.Map; /** * builder for dy query param * * @author zhengdalong * @version V1.0 * @date 2019/1/10 7:39 PM */ public class EsJobQueryParam<Q> { private final Integer shardingItem; private final Integer shardingTotalCount; private final Date queryBeginTime; private final Date queryEndTime; private final Q q; EsJobQueryParam(Builder<Q> builder) { this.shardingItem = builder.shardingItem; this.shardingTotalCount = builder.shardingTotalCount; this.queryBeginTime = builder.queryBeginTime; this.queryEndTime = builder.queryEndTime; this.q = builder.q; } public static class Builder<K2> { Integer shardingItem; Integer shardingTotalCount; Date queryBeginTime; Date queryEndTime; K2 q; private Builder() { } //static K2 ? public static <K3> Builder<K3> builder() { return new Builder<K3>(); } public Builder<K2> setShardingItem(Integer shardingItem) { this.shardingItem = shardingItem; return this; } public Builder<K2> setShardingTotalCount(Integer shardingTotalCount) { this.shardingTotalCount = shardingTotalCount; return this; } public Builder<K2> setQueryBeginTime(Date queryBeginTime) { this.queryBeginTime = queryBeginTime; return this; } public Builder<K2> setQueryEndTime(Date queryEndTime) { this.queryEndTime = queryEndTime; return this; } public Builder<K2> setKey(K2 q) { this.q = q; return this; } public EsJobQueryParam<K2> build() { return new EsJobQueryParam(this); } } private final static String FILED_SHARDING_ITEM = "shardingItem"; private final static String FIELD_SHARDING_TOTAL_COUNT = "shardingTotalCount"; private final static String FIELD_QUERY_BEGIN_TIME = "queryBeginTime"; private final static String FIELD_QUERY_END_TIME = "queryEndTime"; public Map<String, Object> parse() { Map<String, Object> param = new HashMap<>(); param.put(FILED_SHARDING_ITEM, shardingItem); param.put(FIELD_SHARDING_TOTAL_COUNT, shardingTotalCount); param.put(FIELD_QUERY_BEGIN_TIME, queryBeginTime); param.put(FIELD_QUERY_END_TIME, queryEndTime); return param; } public Integer getShardingItem() { return shardingItem; } public Integer getShardingTotalCount() { return shardingTotalCount; } public Date getQueryBeginTime() { return queryBeginTime; } public Date getQueryEndTime() { return queryEndTime; } public Q getQ() { return q; } }
clojure 与 java 交互 (泛型 generics)
Tricks for Java interop
Eric Normand · Updated August 21, 2018
When I first started learning Java, it was very early in its evolution. It was a simple language with classes and methods. Over the years, they''ve added all sorts of stuff. Inner classes, enums, generics, and vararg methods. These things continuously trip me up when writing Clojure. What''s cool is that while Java has added lots of features as a language, the JVM bytecodes have been very stable. These features are compiled into the same basic class/method scheme as everything else.
Luckily, Clojure gives us exactly what we need to consume classes and call methods. Over the years, I''ve had to deal with all of those Java features I listed above. It''s not always obvious how they map. I still have to look these things up all the time. So I''m collecting these tricks here for myself and for you to refer to.
Java class and package names
Ok, this isn''t really a Java language feature issue. But it''s important. Clojure allows characters in names that are not legal in Java names. The most common problem with this is that hyphens in Clojure names get translated into underscores. But there are many more characters that need translating (?
, !
, etc.).
Clojure has a function called clojure.core/munge
that will convert Clojure-style names into their equivalent Java legal name. The reverse operation is called clojure.core/demunge
. These fns actually wrap methods in the Clojure compiler, so they''re the same logic the compiler uses itself. It uses a lookup table to know which characters are allowed and which need to be translated. Refer to that table to see quickly how things will be translated.
Inner classes
If you use a lot of Java interop, you''ll often find a class like this one. It''s called java.lang.Thread.UncaughtExceptionHandler
. The package is java.lang
, and the classname is Thread.UncaughtExceptionHandler
. You know the Thread
part is part of the classname because it starts with a capital T
. But now it means there''s a .
in the name.
This type of class is called an inner class. It means that the class UncaughtExceptionHandler
was defined inside of the Thread
class. That''s not a problem. The problem is that .
(dot) is not a valid character in Java classnames! So how is this thing named?
The answer is that the Java compiler replaces .
s (dots) in the class names with $
(dollar sign). So, when you''re refering to that class, you need to do the same translation:
(reify Thread$UncaughtExceptionHandler
...)
Enums
Java added Enums in Java 5. Enums are a fancy way to represent a choice between different constants. You get a new type (the enum) and you have premade instances of that type made for you. Of course, there''s no bytecode for enums. Instances of the enum are implemented as static fields. So if you''ve got a Java enum like this:
enum DaysOfWeek{ MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY; }
You can refer to those enumerations in Clojure like this:
DaysOfWeek/TUESDAY
That''s the standard static field access.
Varargs methods
Java 5 also added varargs. That is, methods that take different numbers of arguments. Specifically, the last argument can have ...
(three dots) after the type and Java will accept any number of arguments of that type, including zero.
An example is java.util.Formatter.format()
. Here''s the type signature:
public Formatter format(String format, Object... args)
Of course, the JVM bytecode does not know anything about this. The Java compiler has to convert that into something the JVM can handle. What it does is package up all of the arguments into an array, and it passes that array as the argument. So, as far as the JVM is concerned, that format()
method just has two arguments. A String
and an array of Object
s. Note that the array might be empty.
What that means to you, as a Clojure programmer, is that you have to build that array yourself--even if it''s empty.
Building an empty array is easy with clojure.core/make-array
:
(.format formatter "No need for args" (make-array Object 0))
But if you need some stuff in there, that''s easy too with clojure.core/into-array
:
(.format formatter "%d %d %d" (into-array Object [1 2 3]))
Generics
Java 5 also introduced generics. What a release! Generics are a static type system that lets you parameterize classes on other classes. They''re very commonly used for collections, where you want to say you want a list of strings (written List<String>
).
But here''s the thing: the type inference and checking is done entirely at compile time. There''s no representation of this in the bytecode. That means if you need to consume these classes from Clojure, you don''t have to deal with the type in any different way. Just consider all the types to be Object
, which is typically how they''re represented in bytecode anyway.
Now, if you''re creating a library for consumption by people writing Java, you might be used to using gen-class
. But gen-class
does not have any way to specify the generics type signatures. If you really want to include that information, you can write an interface or class in Java directly. Chas Emerick has a good answer on Stack Overflow. I have a lesson on how to include Java code in your Clojure projects.
Conclusions
I''m glad I''ve documented these tricks because I''m likely to encounter them again. These are frustrating quirks. But writing these down has also helped me appreciate the stability of the JVM bytecode. Java has added significant language features, but the JVM is basically the same. Amazing.
The JVM is full of these kinds of quirks. That''s why I created the JVM Fundamentals for Clojure course. You can buy it to view online or to download. Or you can buy a membership and get the JVM course and all of the other courses on the site.
After talking about some quirks, I really want to explore one of the big advantages Clojure gets by running on the JVM. That thing is the highly-optimized garbage collector. Clojure generates a lot of garbage, but it''s vacuumed up quickly by the JVM. That''s what we''ll explore next time.
Delphi 2009 泛型容器单元 (Generics.Collections)[4]: TDictionary
TDictionary 类似哈希表.
本例效果图:
代码文件:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Memo1: TMemo;
Edit1: TEdit;
Edit2: TEdit;
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
uses Generics.Collections; {Delphi 2009 新增的泛型容器单元}
var
Dictionary: TDictionary<Cardinal,string>;
{定义一个泛型 TDictionary 类, 指定有 Cardinal、string 构成}
{建立}
procedure TForm1.FormCreate(Sender: TObject);
begin
Dictionary := TDictionary<Cardinal,string>.Create;
Memo1.Clear;
Button1.Caption := Button1.Caption + '' 添加'';
Button2.Caption := Button2.Caption + '' 删除'';
Button3.Caption := Button3.Caption + '' 尝试取值'';
Button4.Caption := Button4.Caption + '' 清空'';
Edit1.Clear;
Edit2.Clear;
Edit1.NumbersOnly := True;
end;
{释放}
procedure TForm1.FormDestroy(Sender: TObject);
begin
Dictionary.Free;
end;
{添加}
procedure TForm1.Button1Click(Sender: TObject);
var
key: Cardinal;
value: string;
str: string;
k,v: Boolean;
begin
key := StrToIntDef(Edit1.Text, 0);
value := Edit2.Text;
if value = '''' then value := ''Null'';
k := Dictionary.ContainsKey(key); {Key 是否存在}
v := Dictionary.ContainsValue(value); {Value 是否存在}
if not k then
begin
Dictionary.Add(key, value);
Memo1.Lines.Add(Format(''%d=%s'', [key, value])); {同步显示}
end;
if k and not v then
begin
str := Format(''key 已存在: %d=%s; 是否修改其值?'', [key, Dictionary[key]]);
if MessageBox(0, PChar(str), ''提示'', MB_OKCANCEL or MB_ICONQUESTION) = mrOk then
begin
//Dictionary[key] := value; {Dictionary[key] = Dictionary.Item[key]}
Dictionary.AddOrSetValue(key, value); {也可使用上一句}
Memo1.Lines.Values[IntToStr(key)] := value; {同步显示}
end;
end;
if k and v then
begin
str := Format(''%d=%s 已存在, 不能重复添加'', [key, value]);
MessageBox(0, PChar(str), ''错误'', MB_OK + MB_ICONHAND);
end;
Text := IntToStr(Dictionary.Count);
end;
{删除: Remove}
procedure TForm1.Button2Click(Sender: TObject);
var
key: Integer;
i: Integer;
begin
key := StrToIntDef(Edit1.Text, 0);
if not Dictionary.ContainsKey(key) then
begin
ShowMessageFmt(''key: %d 不存在'', [key]);
Exit;
end;
Dictionary.Remove(key);
Text := IntToStr(Dictionary.Count);
{同步显示}
i := Memo1.Lines.IndexOfName(IntToStr(key));
if i > -1 then Memo1.Lines.Delete(i);
end;
{尝试取值: TryGetValue}
procedure TForm1.Button3Click(Sender: TObject);
var
key: Integer;
value: string;
begin
key := StrToIntDef(Edit1.Text, 0);
if Dictionary.TryGetValue(key, value) then
ShowMessageFmt(''key: %d 已存在, 其值是: %s'', [key, value])
else
ShowMessageFmt(''key: %d 不存在'', [key])
end;
{清空: Clear}
procedure TForm1.Button4Click(Sender: TObject);
begin
Dictionary.Clear;
Text := IntToStr(Dictionary.Count);
Memo1.Clear; {同步显示}
end;
end.
窗体文件:
object Form1: TForm1
Left = 0
Top = 0
Caption = ''Form1''
ClientHeight = 165
ClientWidth = 275
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = ''Tahoma''
Font.Style = []
OldCreateOrder = False
Position = poDesktopCenter
OnCreate = FormCreate
OnDestroy = FormDestroy
PixelsPerInch = 96
TextHeight = 13
object Memo1: TMemo
Left = 0
Top = 0
Width = 133
Height = 165
Align = alLeft
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -13
Font.Name = ''Courier New''
Font.Style = []
Lines.Strings = (
''Memo1'')
ParentFont = False
ScrollBars = ssBoth
TabOrder = 0
ExplicitHeight = 176
end
object Button1: TButton
Left = 139
Top = 40
Width = 128
Height = 25
Caption = ''Button1''
TabOrder = 1
OnClick = Button1Click
end
object Button2: TButton
Left = 139
Top = 71
Width = 128
Height = 25
Caption = ''Button2''
TabOrder = 2
OnClick = Button2Click
end
object Button3: TButton
Left = 139
Top = 102
Width = 128
Height = 25
Caption = ''Button3''
TabOrder = 3
OnClick = Button3Click
end
object Edit1: TEdit
Left = 139
Top = 8
Width = 40
Height = 21
TabOrder = 4
Text = ''Edit1''
end
object Edit2: TEdit
Left = 185
Top = 8
Width = 81
Height = 21
TabOrder = 5
Text = ''Edit2''
end
object Button4: TButton
Left = 139
Top = 133
Width = 128
Height = 25
Caption = ''Button4''
TabOrder = 6
OnClick = Button4Click
end
end
我们今天的关于Swift Generics – 调用哪种方法?和swift 方法调用原理的分享已经告一段落,感谢您的关注,如果您想了解更多关于
本文标签: